/**
 * Copyright (c) 2018-2022, NXOS Development Team
 * SPDX-License-Identifier: Apache-2.0
 * 
 * Contains: user call system function
 * 
 * Change Logs:
 * Date           Author            Notes
 * 2022-1-31      JasonHu           Init
 */

#include <base/syscall.h>
#include <base/thread.h>
#define NX_LOG_LEVEL NX_LOG_INFO
#define NX_LOG_NAME "syscall"
#include <base/log.h>
#include <base/process.h>
#include <base/debug.h>
#include <base/thread.h>
#include <base/fs.h>
#include <base/hub.h>
#include <base/vmspace.h>
#include <base/uaccess.h>
#include <base/page.h>
#include <base/string.h>
#include <base/snapshot.h>
#include <base/time.h>
#include <base/malloc.h>
#include <base/driver.h>
#include <base/signal.h>
#include <base/timer.h>
#include <base/sharemem.h>
#include <base/irq.h>
#include <base/context.h>
#include <base/sched.h>
#include <base/ipc.h>
#include <base/fifo.h>
#include <base/condition.h>
#include <base/msgqueue.h>
#include <base/pipe.h>
#include <base/trace.h>

#include "process_impl.h"

NX_PRIVATE int SysInvalidCall(void)
{
    //NX_Thread *cur = NX_ThreadSelf();
    //NX_LOG_E("thread %s/%d call invalid syscall!", cur->name, cur->tid);    
    return 0;
}

NX_PRIVATE int SysDebugLog(const char *buf, int size)
{
    /* FIXME: check buf -> buf + size accessable */
    NX_Printf(buf);
    return 0;
}

NX_PRIVATE void SysProcessExit(NX_U32 exitCode)
{
    NX_ProcessExit(exitCode);
    NX_PANIC("process exit syscall failed !");
}

NX_PRIVATE NX_Error SysProcessLaunch(char *path, NX_U32 flags, NX_U32 *outExitCode,
    char *cmd, char *env, NX_Solt * outSolt)
{
    NX_U32 exitCode = 0;
    NX_Error err = NX_EOK;
    NX_Solt solt = NX_SOLT_INVALID_VALUE;
    err = NX_ProcessLaunch(path, flags, &exitCode, cmd, env, &solt);
    if (outExitCode)
    {
        NX_CopyToUser((char *)outExitCode, (char *)&exitCode, sizeof(exitCode));
    }
    if (outSolt)
    {
        NX_CopyToUser((char *)outSolt, (char *)&solt, sizeof(solt));
    }
    return err;
}

NX_PRIVATE NX_Error SysFileSystemMount(const char * dev, const char * dir, const char * fsname, NX_U32 flags)
{
    return NX_FileSystemMount(dev, dir, fsname, flags);
}

NX_PRIVATE NX_Error SysFileSystemUnmount(const char * path)
{
    return NX_FileSystemUnmount(path);
}

NX_PRIVATE NX_Error SysFileSystemSync(void)
{
    return NX_FileSystemSync();
}

NX_PRIVATE NX_ExposedObject *GetExobjBySolt(NX_Solt solt)
{
    NX_Process * process;

    if (solt == NX_SOLT_INVALID_VALUE)
    {
        return NX_NULL;
    }

    process = NX_ProcessCurrent();
    return NX_ProcessGetSolt(process, solt);
}

NX_PRIVATE NX_Error SoltToFile(NX_Solt solt, NX_SSize * fd)
{
    NX_Process * process;
    NX_ExposedObject * exobj;

    if (solt == NX_SOLT_INVALID_VALUE)
    {
        return NX_EINVAL;
    }

    process = NX_ProcessCurrent();
    if ((exobj = NX_ProcessGetSolt(process, solt)) == NX_NULL)
    {
        return NX_ENOSRCH;
    }

    if (exobj->type != NX_EXOBJ_FILE)
    {
        return NX_ENORES;
    }

    *fd = (NX_SSize)exobj->object;
    NX_ASSERT(*fd);

    return NX_EOK;
}

NX_PRIVATE NX_Error FileCloseSolt(void * object, NX_ExposedObjectType type)
{
    NX_SSize fd;

    if (type != NX_EXOBJ_FILE)
    {
        return NX_ENORES;
    }

    fd = (NX_SSize)object;
    NX_ASSERT(fd);

    return NX_FileClose(fd);
}

NX_PRIVATE NX_Error SysFileOpen(const char * path, NX_U32 flags, NX_U32 mode, NX_Solt * outSolt)
{
    NX_SSize fd;
    NX_Error err = NX_EOK;
    NX_Solt solt = NX_SOLT_INVALID_VALUE;
    NX_Process * process;
    
    if (!path || !outSolt)
    {
        return NX_EINVAL;
    }
    
    if ((fd = NX_FileOpen(path, flags, mode, &err)) < 0)
    {
        return err;
    }

    process = NX_ProcessCurrent();
    if ((err = NX_ProcessInstallSolt(process, (NX_SSize *)fd, NX_EXOBJ_FILE, FileCloseSolt, &solt)) != NX_EOK)
    {
        NX_FileClose(fd);
        return err;
    }

    NX_CopyToUser((char *)outSolt, (char *)&solt, sizeof(solt));

    return NX_EOK;
}

NX_PRIVATE NX_Error DoFileRead(NX_ExposedObject *obj, void * buf, NX_Size len, NX_Size *outLen)
{
    NX_Error err;
    NX_Size size;
    NX_SSize fd = -1;

    fd = (NX_SSize)obj->object;
    NX_ASSERT(fd >= 0);

    size = NX_FileRead(fd, buf, len, &err);
    if (err != NX_EOK)
    {
        return err;
    }
    NX_CopyToUser((char *)outLen, (char *)&size, sizeof(size));

    return NX_EOK;
}

NX_PRIVATE NX_Error DoPipeRecv(NX_ExposedObject *obj, void * buf, NX_Size len, NX_Size *outLen)
{
    NX_Error err;
    NX_Size size;
    NX_Pipe * pipe;

    pipe = (NX_Pipe *)obj->object;
    NX_ASSERT(pipe);

    err = NX_PipeRecv(pipe, buf, len, &size);
    if (err == NX_EOK)
    {
        NX_CopyToUser((char *)outLen, (char *)&size, sizeof(NX_Size));
    }
    
    return err;
}

NX_PRIVATE NX_Error SysFileRead(NX_Solt solt, void * buf, NX_Size len, NX_Size *outLen)
{
    NX_ExposedObject *obj;

    obj = GetExobjBySolt(solt);
    if (!obj)
    {
        return NX_EINVAL;
    }

    switch (obj->type)
    {
    case NX_EXOBJ_FILE:
        return DoFileRead(obj, buf, len, outLen);
    case NX_EXOBJ_PIPE:
        return DoPipeRecv(obj, buf, len, outLen);
    
    default:
        break;
    }
    return NX_EINVAL;
}

NX_PRIVATE NX_Error DoFileWrite(NX_ExposedObject *obj, void * buf, NX_Size len, NX_Size *outLen)
{
    NX_Error err;
    NX_Size size;
    NX_SSize fd = -1;

    fd = (NX_SSize)obj->object;
    NX_ASSERT(fd >= 0);

    size = NX_FileWrite(fd, buf, len, &err);
    if (err != NX_EOK)
    {
        return err;
    }
    NX_CopyToUser((char *)outLen, (char *)&size, sizeof(size));

    return NX_EOK;
}

NX_PRIVATE NX_Error DoPipeSend(NX_ExposedObject *obj, void * buf, NX_Size len, NX_Size *outLen)
{
    NX_Error err;
    NX_Size size;
    NX_Pipe * pipe;

    pipe = (NX_Pipe *)obj->object;
    NX_ASSERT(pipe);

    err = NX_PipeSend(pipe, buf, len, &size);
    if (err == NX_EOK)
    {
        NX_CopyToUser((char *)outLen, (char *)&size, sizeof(NX_Size));
    }
    
    return err;
}

NX_PRIVATE NX_Error SysFileWrite(NX_Solt solt, void * buf, NX_Size len, NX_Size *outLen)
{
    NX_ExposedObject *obj;

    obj = GetExobjBySolt(solt);
    if (!obj)
    {
        return NX_EINVAL;
    }

    switch (obj->type)
    {
    case NX_EXOBJ_FILE:
        return DoFileWrite(obj, buf, len, outLen);
    case NX_EXOBJ_PIPE:
        return DoPipeSend(obj, buf, len, outLen);
    
    default:
        break;
    }
    return NX_EINVAL;
}

NX_Error SysFileIoctl(NX_Solt solt, NX_U32 cmd, void *arg)
{
    NX_Error err;
    NX_SSize fd = -1;
    
    err = SoltToFile(solt, &fd);
    if (err != NX_EOK)
    {
        return err;
    }

    return NX_FileIoctl(fd, cmd, arg);
}

NX_PRIVATE NX_Offset SysFileSetPointer(NX_Solt solt, NX_Offset off, int whence, NX_Error *outErr)
{
    NX_Error err;
    NX_SSize fd = -1;
    
    err = SoltToFile(solt, &fd);
    if (err != NX_EOK)
    {
        return err;
    }

    return NX_FileSetPointer(fd, off, whence, outErr);
}

NX_PRIVATE NX_Error SysFileSync(NX_Solt solt)
{
    NX_Error err;
    NX_SSize fd = -1;
    
    err = SoltToFile(solt, &fd);
    if (err != NX_EOK)
    {
        return err;
    }

    return NX_FileSync(fd);
}

NX_PRIVATE NX_Error SysFileSetMode(NX_Solt solt, NX_U32 mode)
{
    NX_Error err;
    NX_SSize fd = -1;
    
    err = SoltToFile(solt, &fd);
    if (err != NX_EOK)
    {
        return err;
    }

    return NX_FileSetMode(fd, mode);
}

NX_PRIVATE NX_Error SysFileGetStat(NX_Solt solt, NX_FileStatInfo * st)
{
    NX_Error err;
    NX_SSize fd = -1;
    
    err = SoltToFile(solt, &fd);
    if (err != NX_EOK)
    {
        return err;
    }

    return NX_FileGetStat(fd, st);
}

NX_PRIVATE NX_Error SoltToDir(NX_Solt solt, NX_SSize * fd)
{
    NX_Process * process;
    NX_ExposedObject * exobj;

    if (solt == NX_SOLT_INVALID_VALUE)
    {
        return NX_EINVAL;
    }

    process = NX_ProcessCurrent();
    if ((exobj = NX_ProcessGetSolt(process, solt)) == NX_NULL)
    {
        return NX_ENOSRCH;
    }

    if (exobj->type != NX_EXOBJ_DIR)
    {
        return NX_ENORES;
    }

    *fd = (NX_SSize)exobj->object;
    NX_ASSERT(*fd);

    return NX_EOK;
}

NX_PRIVATE NX_Error DirCloseSolt(void * object, NX_ExposedObjectType type)
{
    NX_SSize fd;

    if (type != NX_EXOBJ_DIR)
    {
        return NX_ENORES;
    }

    fd = (NX_SSize) object;
    NX_ASSERT(fd);

    return NX_DirClose(fd);
}

NX_PRIVATE NX_Error SysDirOpen(const char * path, NX_Solt * outSolt)
{
    NX_SSize fd;
    NX_Error err = NX_EOK;
    NX_Solt solt = NX_SOLT_INVALID_VALUE;
    NX_Process * process;
    
    if (!path || !outSolt)
    {
        return NX_EINVAL;
    }
    
    if ((fd = NX_DirOpen(path, &err)) < 0)
    {
        return err;
    }

    process = NX_ProcessCurrent();
    if ((err = NX_ProcessInstallSolt(process, (void *)fd, NX_EXOBJ_DIR, DirCloseSolt, &solt)) != NX_EOK)
    {
        NX_DirClose(fd);
        return err;
    }

    NX_CopyToUser((char *)outSolt, (char *)&solt, sizeof(solt));

    return NX_EOK;
}

NX_PRIVATE NX_Error SysDirRead(NX_Solt solt, NX_Dirent * dir)
{
    NX_Error err;
    NX_SSize fd = -1;
    
    err = SoltToDir(solt, &fd);
    if (err != NX_EOK)
    {
        return err;
    }

    return NX_DirRead(fd, dir);
}

NX_PRIVATE NX_Error SysDirResetPointer(NX_Solt solt)
{
    NX_Error err;
    NX_SSize fd = -1;
    
    err = SoltToDir(solt, &fd);
    if (err != NX_EOK)
    {
        return err;
    }

    return NX_DirResetPointer(fd);
}

NX_PRIVATE NX_Error SysDirCreate(const char * path, NX_U32 mode)
{
    return NX_DirCreate(path, mode);
}

NX_PRIVATE NX_Error SysDirDelete(const char * path)
{
    return NX_DirDelete(path);
}

NX_PRIVATE NX_Error SysFileRename(const char * src, const char * dst)
{
    return NX_FileRename(src, dst);
}

NX_PRIVATE NX_Error SysFileDelete(const char * path)
{
    return NX_FileDelete(path);
}

NX_PRIVATE NX_Error SysFileAccess(const char * path, NX_U32 mode)
{
    return NX_FileAccess(path, mode);
}

NX_PRIVATE NX_Error SysFileSetModeToPath(const char * path, NX_U32 mode)
{
    return NX_FileSetModeToPath(path, mode);
}

NX_PRIVATE NX_Error SysFileGetStatFromPath(const char * path, NX_FileStatInfo * st)
{
    return NX_FileGetStatFromPath(path, st);
}

NX_PRIVATE NX_Error HubCloseSolt(void * object, NX_ExposedObjectType type)
{
    NX_Hub * hub;

    if (type != NX_EXOBJ_HUB)
    {
        return NX_ENORES;
    }

    hub = (NX_Hub *) object;
    NX_ASSERT(hub);
    return NX_HubUnregister(hub->name);
}

NX_PRIVATE NX_Error SysHubRegister(const char *name, NX_Size maxClient)
{
    NX_Hub * hub = NX_NULL;
    NX_Error err;
    NX_Solt solt;

    if ((err = NX_HubRegister(name, maxClient, &hub)) != NX_EOK)
    {
        return err;
    }

    /* install hub */
    solt = NX_SOLT_INVALID_VALUE;
    if ((err = NX_ProcessInstallSolt(NX_ProcessCurrent(), hub, NX_EXOBJ_HUB, HubCloseSolt, &solt)) != NX_EOK)
    {
        NX_LOG_E("install hub object failed!");
        NX_HubUnregister(name);
        return NX_ENORES;
    }
    return err;
}

NX_PRIVATE NX_Error SysHubUnregister(const char *name)
{
    NX_Hub * hub;
    NX_Error err;
    NX_Process * process;
    NX_Solt solt;

    if ((hub = NX_HubSearch(name)) == NX_NULL)
    {
        return NX_ENOSRCH;
    }

    process = NX_ProcessCurrent();
    if (process == NX_NULL)
    {
        return NX_ENORES;
    }

    solt = NX_ProcessLocateSolt(process, hub, NX_EXOBJ_HUB);
    if (solt == NX_SOLT_INVALID_VALUE)
    {
        return NX_ENORES;
    }

    if ((err = NX_ProcessUninstallSolt(process, solt)) != NX_EOK)
    {
        return err;
    }

    return NX_HubUnregister(name);
}

NX_PRIVATE NX_Error SysHubCallParamName(const char *name, NX_HubParam *param, NX_Size *retVal)
{
    return NX_HubCallParamName(name, param, retVal);
}

NX_PRIVATE NX_Error SysHubReturn(NX_Size retVal, NX_Error retErr)
{
    return NX_HubReturn(retVal, retErr);
}

NX_PRIVATE NX_Error SysHubWait(NX_HubParam *param, NX_U32 flags)
{
    return NX_HubWait(param, flags);
}

NX_PRIVATE void *SysHubTranslate(void *addr, NX_Size size)
{
    return NX_HubTranslate(addr, size);
}

NX_PRIVATE void *SysMemMap(void * addr, NX_Size length, NX_U32 prot, NX_Error *outErr)
{
    NX_Error err;
    NX_Thread *self;
    void *outAddr = NX_NULL;
    NX_UArch attr;

    if (!length || !prot)
    {
        err = NX_EINVAL;
        if (outErr)
        {
            NX_CopyToUser((char *)outErr, (char *)&err, sizeof(NX_Error));
        }
        return NX_NULL;
    }

    self = NX_ThreadSelf();

    /* make attr */
    attr = NX_PAGE_ATTR_USER & (~NX_PAGE_ATTR_RWX);
    if (prot & NX_PROT_READ)
    {
        attr |= NX_PAGE_ATTR_READ;
    }
    if (prot & NX_PROT_WRITE)
    {
        attr |= NX_PAGE_ATTR_WRITE;
    }
    if (prot & NX_PROT_EXEC)
    {
        attr |= NX_PAGE_ATTR_EXEC;
    }

    err = NX_VmspaceMap(&self->resource.process->vmspace,
                        (NX_Addr)addr, length, attr, 0, &outAddr);

    if (outErr)
    {
        NX_CopyToUser((char *)outErr, (char *)&err, sizeof(NX_Error));
    }
    return outAddr;
}

NX_PRIVATE void *SysMemMap2(void * addr, void * phyAddr, NX_Size length, NX_U32 prot, NX_Error *outErr)
{
    NX_Error err;
    NX_Thread *self;
    void *outAddr = NX_NULL;
    NX_UArch attr;

    if (!length || !prot)
    {
        err = NX_EINVAL;
        if (outErr)
        {
            NX_CopyToUser((char *)outErr, (char *)&err, sizeof(NX_Error));
        }
        return NX_NULL;
    }

    self = NX_ThreadSelf();

    /* make attr */
    attr = NX_PAGE_ATTR_USER & (~NX_PAGE_ATTR_RWX);
    if (prot & NX_PROT_READ)
    {
        attr |= NX_PAGE_ATTR_READ;
    }
    if (prot & NX_PROT_WRITE)
    {
        attr |= NX_PAGE_ATTR_WRITE;
    }
    if (prot & NX_PROT_EXEC)
    {
        attr |= NX_PAGE_ATTR_EXEC;
    }

    err = NX_VmspaceMapWithPhy(&self->resource.process->vmspace,
                        (NX_Addr)addr, (NX_Addr)phyAddr, length, attr, 0, &outAddr);

    if (outErr)
    {
        NX_CopyToUser((char *)outErr, (char *)&err, sizeof(NX_Error));
    }
    return outAddr;
}

NX_PRIVATE NX_Error SysMemUnmap(void *addr, NX_Size length)
{
    NX_Thread *self;
    
    if (addr == NX_NULL || !length)
    {
        return NX_EINVAL;
    }

    self = NX_ThreadSelf();

    return NX_VmspaceUnmap(&self->resource.process->vmspace, NX_PAGE_ALIGNDOWN((NX_Addr)addr), length, 0);
}

NX_PRIVATE void *SysMemHeap(void *addr, NX_Error *outErr)
{
    NX_Error err;
    NX_Thread *self;
    void *heapAddr;

    self = NX_ThreadSelf();

    heapAddr = NX_VmspaceUpdateHeap(&self->resource.process->vmspace, (NX_Addr)addr, &err);
    if (outErr)
    {
        NX_CopyToUser((char *)outErr, (char *)&err, sizeof(NX_Error));
    }
    return heapAddr;
}

NX_PRIVATE NX_Error SysProcessGetCwd(char * buf, NX_Size length)
{
    char * cwd;

    if (!buf || !length)
    {
        return NX_EINVAL;
    }

    cwd = NX_ProcessGetCwd(NX_ThreadSelf()->resource.process);
    if (!cwd)
    {
        return NX_ENORES;
    }

    NX_CopyToUser(buf, cwd, NX_MIN((int)length, NX_StrLen(cwd)));
    return NX_EOK;
}

NX_PRIVATE NX_Error SysProcessSetCwd(char * buf)
{
    char * cwd;
    NX_Process * process;

    if (!buf)
    {
        return NX_EINVAL;
    }

    process = NX_ThreadSelf()->resource.process;

    cwd = NX_ProcessGetCwd(process);
    if (!cwd)
    {
        return NX_ENORES;
    }

    return NX_ProcessSetCwd(process, buf);
}

NX_PRIVATE NX_Error SysSoltClose(NX_Solt solt)
{
    return NX_ProcessUninstallSolt(NX_ProcessCurrent(), solt);
}

NX_PRIVATE NX_Solt SysSnapshotCreate(NX_U32 snapshotType, NX_U32 flags, NX_Error * outErr)
{
    return NX_SnapshotCreate(snapshotType, flags, outErr);
}

NX_PRIVATE NX_Error SysSnapshotFirst(NX_Solt solt, void * object)
{
    return NX_SnapshotFirst(solt, object);
}

NX_PRIVATE NX_Error SysSnapshotNext(NX_Solt solt, void * object)
{
    return NX_SnapshotNext(solt, object);
}

NX_PRIVATE NX_Error SysThreadSleep(NX_UArch microseconds)
{
    return NX_ThreadSleep(microseconds);
}

NX_PRIVATE NX_TimeVal SysClockGetMillisecond(void)
{
    return NX_ClockGetMillisecond();
}

NX_PRIVATE NX_Error SysTimeSet(NX_Time * time)
{
    return NX_TimeSet(time);
}

NX_PRIVATE NX_Error SysTimeGet(NX_Time * time)
{
    return NX_TimeGet(time);
}

NX_PRIVATE NX_U32 CheckUserThreadPriority(NX_U32 priority)
{
    if ((priority & NX_THREAD_PRIORITY_FLAG) == NX_THREAD_PRIORITY_FLAG)
    {
        switch (priority)
        {
        case NX_THREAD_PRIORITY_USER_IDLE:
            priority = NX_THREAD_PRIORITY_IDLE;
            break;
        case NX_THREAD_PRIORITY_USER_LOW:
            priority = NX_THREAD_PRIORITY_LOW;
            break;
        case NX_THREAD_PRIORITY_USER_NORMAL:
            priority = NX_THREAD_PRIORITY_NORMAL;
            break;
        case NX_THREAD_PRIORITY_USER_HIGH:
            priority = NX_THREAD_PRIORITY_HIGH;
            break;
        case NX_THREAD_PRIORITY_USER_MAX:
            priority = NX_THREAD_PRIORITY_MAX;
            break;
        default:
            break;
        }
        priority &= ~NX_THREAD_PRIORITY_FLAG;
    }

    priority &= NX_THREAD_PRIORITY_MASK;

    /* fix priority */
    if (priority <= NX_THREAD_PRIORITY_IDLE)
    {
        priority = NX_THREAD_PRIORITY_IDLE;
    }

    if (priority > NX_THREAD_PRIORITY_MAX)
    {
        priority = NX_THREAD_PRIORITY_MAX;
    }
    return priority;
}

NX_PRIVATE NX_Error SysThreadCreate(NX_ThreadAttr * attr, NX_ThreadHandler handler, void * arg, NX_U32 flags, NX_Solt * outSolt)
{
    NX_ThreadAttr threadAttr;
    void * stackBase = NX_NULL;
    NX_Process * process;
    NX_Error err;
    NX_Thread * thread;
    NX_Solt solt;
    NX_U32 exitCode = 0;
    NX_U32 priority;

    if (!attr || !handler)
    {
        return NX_EINVAL;
    }

    NX_CopyFromUser((char *)&threadAttr, (char *)attr, sizeof(threadAttr));

    if (!threadAttr.stackSize)
    {
        return NX_EINVAL;
    }

    /* check user priority */
    priority = CheckUserThreadPriority(threadAttr.schedPriority);

    process = NX_ProcessCurrent();
    if (!process)
    {
        NX_LOG_E("create thread no process!");
        return NX_EFAULT;
    }

    /* map stack */
    err = NX_VmspaceMap(&process->vmspace, 0, threadAttr.stackSize, NX_PAGE_ATTR_USER, 0, &stackBase);
    if (err != NX_EOK)
    {
        NX_LOG_E("map user stack error!");
        return err;
    }
    NX_ASSERT(stackBase);

    /* create thread */
    thread = NX_ThreadCreate("uthread", NX_UserThreadEntry, NX_NULL, priority);
    if (thread == NX_NULL)
    {
        NX_LOG_E("create thread error!");
        NX_VmspaceUnmap(&process->vmspace, (NX_Addr)stackBase, threadAttr.stackSize, 0);
        return NX_ENORES;
    }

    thread->userHandler = handler;
    thread->userStackSize = threadAttr.stackSize;
    thread->userStackBase = stackBase;
    thread->userArg = arg;

    /* map tls */
    if (NX_ProcessMapTls(process, thread) != NX_EOK)
    {
        NX_LOG_E("map thread tls error!");
        NX_ThreadDestroy(thread);
        NX_VmspaceUnmap(&process->vmspace, (NX_Addr)stackBase, threadAttr.stackSize, 0);
        return NX_ENOMEM;
    }

    /* install thread solt */
    solt = NX_SOLT_INVALID_VALUE;
    if (NX_ProcessInstallSolt(process, thread, NX_EXOBJ_THREAD, NX_NULL, &solt) != NX_EOK)
    {
        NX_LOG_E("install thread object failed!");
        NX_ProcessUnmapTls(process, thread);
        NX_ThreadDestroy(thread);
        NX_VmspaceUnmap(&process->vmspace, (NX_Addr)stackBase, threadAttr.stackSize, 0);
        return NX_ENORES;
    }

    if (outSolt)
    {
        NX_CopyToUser((char *)outSolt, (char *)&solt, sizeof(solt));
    }

    /* process attach thread */
    NX_ProcessAppendThread(process, thread);

    /* start thread */
    if (!(flags & NX_THREAD_CREATE_SUSPEND))
    {
        NX_ThreadStart(thread);
    }
    
    if (flags & NX_THREAD_CREATE_WAIT)
    {
        return NX_ThreadWait(thread, &exitCode);
    }

    return NX_EOK;
}

NX_PRIVATE void SysThreadExit(NX_U32 exitCode)
{
    NX_ThreadExit(exitCode);
}

NX_PRIVATE NX_Error SysThreadSuspend(NX_Solt solt)
{
    NX_ExposedObject * exobj;
    NX_Thread * thread;

    if (solt == NX_SOLT_INVALID_VALUE)
    {
        return NX_EINVAL;
    }

    exobj = NX_ProcessGetSolt(NX_ProcessCurrent(), solt);
    if (exobj == NX_NULL)
    {
        return NX_ENOSRCH;
    }

    if (exobj->type != NX_EXOBJ_THREAD)
    {
        return NX_ENORES;
    }
    thread = (NX_Thread *)exobj->object;
    NX_ASSERT(thread);
    if (thread->state == NX_THREAD_INIT || thread->state == NX_THREAD_EXIT)
    {
        return NX_EPERM;    
    }
    return NX_ThreadBlock(thread);
}

NX_PRIVATE NX_Error SysThreadResume(NX_Solt solt)
{
    NX_ExposedObject * exobj;
    NX_Thread * thread;

    if (solt == NX_SOLT_INVALID_VALUE)
    {
        return NX_EINVAL;
    }

    exobj = NX_ProcessGetSolt(NX_ProcessCurrent(), solt);
    if (exobj == NX_NULL)
    {
        return NX_ENOSRCH;
    }

    if (exobj->type != NX_EXOBJ_THREAD)
    {
        return NX_ENORES;
    }
    thread = (NX_Thread *)exobj->object;
    NX_ASSERT(thread);
    if (thread->state != NX_THREAD_BLOCKED && thread->state != NX_THREAD_INIT)
    {
        return NX_EPERM;
    }
    if (thread->state == NX_THREAD_INIT)
    {
        return NX_ThreadStart(thread);
    }
    
    return NX_ThreadUnblock(thread);
}

NX_PRIVATE NX_Error SysThreadWait(NX_Solt solt, NX_U32 * outExitCode)
{
    NX_ExposedObject * exobj;
    NX_Error err;
    NX_U32 exitCode;

    if (solt == NX_SOLT_INVALID_VALUE)
    {
        return NX_EINVAL;
    }

    exobj = NX_ProcessGetSolt(NX_ProcessCurrent(), solt);
    if (exobj == NX_NULL)
    {
        return NX_ENOSRCH;
    }

    if (exobj->type != NX_EXOBJ_THREAD)
    {
        return NX_ENORES;
    }

    err = NX_ThreadWait(exobj->object, &exitCode);
    if (err != NX_EOK)
    {
        return err;
    }

    if (outExitCode)
    {
        NX_CopyToUser((char *)outExitCode, (char *)&exitCode, sizeof(exitCode));
    }
    
    return NX_EOK;
}

NX_PRIVATE NX_Error SysThreadTerminate(NX_Solt solt, NX_U32 exitCode)
{
    NX_ExposedObject * exobj;
    NX_Error err;

    if (solt == NX_SOLT_INVALID_VALUE)
    {
        return NX_EINVAL;
    }

    exobj = NX_ProcessGetSolt(NX_ProcessCurrent(), solt);
    if (exobj == NX_NULL)
    {
        return NX_ENOSRCH;
    }

    if (exobj->type != NX_EXOBJ_THREAD)
    {
        return NX_ENORES;
    }

    err = NX_ThreadTerminate(exobj->object, exitCode);
    if (err != NX_EOK)
    {
        return err;
    }
    return NX_EOK;
}

NX_PRIVATE NX_Error SysThreadGetId(NX_Solt solt, NX_U32 * outId)
{
    NX_ExposedObject * exobj;
    NX_Thread * thread;

    if (solt == NX_SOLT_INVALID_VALUE || !outId)
    {
        return NX_EINVAL;
    }

    exobj = NX_ProcessGetSolt(NX_ProcessCurrent(), solt);
    if (exobj == NX_NULL)
    {
        return NX_ENOSRCH;
    }

    if (exobj->type != NX_EXOBJ_THREAD)
    {
        return NX_ENORES;
    }

    thread = (NX_Thread *)exobj->object;

    NX_CopyToUser((char *)outId, (char *)&thread->tid, sizeof(thread->tid));

    return NX_EOK;
}

NX_PRIVATE NX_U32 SysThreadGetCurrentId(void)
{
    NX_Thread * self;

    self = NX_ThreadSelf();
    return (NX_U32)self->tid;
}

NX_PRIVATE NX_Error SysThreadGetCurrent(NX_Solt * outSolt)
{
    NX_Solt solt;
    NX_Process * process;
    NX_Thread * self;
    
    if (!outSolt)
    {
        return NX_EINVAL;
    }

    self = NX_ThreadSelf();
    process = NX_ProcessCurrent();
    if (!process)
    {
        return NX_EFAULT;
    }

    solt = NX_ProcessLocateSolt(process, self, NX_EXOBJ_THREAD);
    if (solt == NX_SOLT_INVALID_VALUE)
    {
        return NX_ENORES;
    }

    NX_CopyToUser((char *)outSolt, (char *)&solt, sizeof(solt));

    return NX_EOK;
}

NX_PRIVATE NX_Error SysThreadGetProcessId(NX_Solt solt, NX_U32 * outId)
{
    NX_ExposedObject * exobj;
    NX_Thread * thread;
    NX_Process * process;

    if (solt == NX_SOLT_INVALID_VALUE || !outId)
    {
        return NX_EINVAL;
    }

    exobj = NX_ProcessGetSolt(NX_ProcessCurrent(), solt);
    if (exobj == NX_NULL)
    {
        return NX_ENOSRCH;
    }

    if (exobj->type != NX_EXOBJ_THREAD)
    {
        return NX_ENORES;
    }

    thread = (NX_Thread *)exobj->object;
    
    process = thread->resource.process;

    NX_CopyToUser((char *)outId, (char *)&process->pid, sizeof(process->pid));

    return NX_EOK;
}

NX_PRIVATE NX_Error SemaphoreCloseSolt(void * object, NX_ExposedObjectType type)
{
    NX_Semaphore * sem;

    if (type != NX_EXOBJ_SEMAPHORE)
    {
        return NX_ENORES;
    }

    sem = (NX_Semaphore *) object;
    NX_ASSERT(sem);

    NX_MemZero(sem, sizeof(sem));
    NX_MemFree(sem);

    return NX_EOK;
}

NX_PRIVATE NX_Error SysSemaphoreCreate(NX_IArch value, NX_Solt * outSolt)
{
    NX_Semaphore * sem;
    NX_Error err;
    NX_Solt solt = NX_SOLT_INVALID_VALUE;
    NX_Process * process;
    
    if (!outSolt)
    {
        return NX_EINVAL;
    }

    sem = NX_MemAlloc(sizeof(NX_Semaphore));
    if (sem == NX_NULL)
    {
        return NX_ENOMEM;
    }

    if ((err = NX_SemaphoreInit(sem, value)) != NX_EOK)
    {
        NX_MemFree(sem);
        return err;
    }

    process = NX_ProcessCurrent();
    if ((err = NX_ProcessInstallSolt(process, sem, NX_EXOBJ_SEMAPHORE, SemaphoreCloseSolt, &solt)) != NX_EOK)
    {
        NX_MemFree(sem);
        return err;
    }

    NX_CopyToUser((char *)outSolt, (char *)&solt, sizeof(solt));

    return NX_EOK;
}

NX_PRIVATE NX_Error SysSemaphoreDestroy(NX_Solt solt)
{
    NX_Semaphore * sem;
    NX_Error err;
    NX_Process * process;
    NX_ExposedObject * exobj;
    
    if (solt == NX_SOLT_INVALID_VALUE)
    {
        return NX_EINVAL;
    }

    process = NX_ProcessCurrent();
    if ((exobj = NX_ProcessGetSolt(process, solt)) == NX_NULL)
    {
        return NX_ENOSRCH;
    }

    if (exobj->type != NX_EXOBJ_SEMAPHORE)
    {
        return NX_ENORES;
    }

    sem = (NX_Semaphore *)exobj->object;
    NX_ASSERT(sem);

    if ((err = NX_ProcessUninstallSolt(process, solt)) != NX_EOK)
    {
        return err;
    }

    NX_MemZero(sem, sizeof(sem));
    NX_MemFree(sem);

    return NX_EOK;
}

NX_PRIVATE NX_Error SysSemaphoreWait(NX_Solt solt)
{
    NX_Semaphore * sem;
    NX_Error err;
    NX_Process * process;
    NX_ExposedObject * exobj;
    
    if (solt == NX_SOLT_INVALID_VALUE)
    {
        return NX_EINVAL;
    }

    process = NX_ProcessCurrent();
    if ((exobj = NX_ProcessGetSolt(process, solt)) == NX_NULL)
    {
        return NX_ENOSRCH;
    }

    if (exobj->type != NX_EXOBJ_SEMAPHORE)
    {
        return NX_ENORES;
    }

    sem = (NX_Semaphore *)exobj->object;
    NX_ASSERT(sem);

    if ((err = NX_SemaphoreWait(sem)) != NX_EOK)
    {
        return err;
    }

    return NX_EOK;
}

NX_PRIVATE NX_Error SysSemaphoreTryWait(NX_Solt solt)
{
    NX_Semaphore * sem;
    NX_Error err;
    NX_Process * process;
    NX_ExposedObject * exobj;
    
    if (solt == NX_SOLT_INVALID_VALUE)
    {
        return NX_EINVAL;
    }

    process = NX_ProcessCurrent();
    if ((exobj = NX_ProcessGetSolt(process, solt)) == NX_NULL)
    {
        return NX_ENOSRCH;
    }

    if (exobj->type != NX_EXOBJ_SEMAPHORE)
    {
        return NX_ENORES;
    }

    sem = (NX_Semaphore *)exobj->object;
    NX_ASSERT(sem);

    if ((err = NX_SemaphoreTryWait(sem)) != NX_EOK)
    {
        return err;
    }

    return NX_EOK;
}

NX_PRIVATE NX_Error SysSemaphoreSignal(NX_Solt solt)
{
    NX_Semaphore * sem;
    NX_Error err;
    NX_Process * process;
    NX_ExposedObject * exobj;
    
    if (solt == NX_SOLT_INVALID_VALUE)
    {
        return NX_EINVAL;
    }

    process = NX_ProcessCurrent();
    if ((exobj = NX_ProcessGetSolt(process, solt)) == NX_NULL)
    {
        return NX_ENOSRCH;
    }

    if (exobj->type != NX_EXOBJ_SEMAPHORE)
    {
        return NX_ENORES;
    }

    sem = (NX_Semaphore *)exobj->object;
    NX_ASSERT(sem);

    if ((err = NX_SemaphoreSignal(sem)) != NX_EOK)
    {
        return err;
    }

    return NX_EOK;
}

NX_PRIVATE NX_Error SysSemaphoreSignalAll(NX_Solt solt)
{
    NX_Semaphore * sem;
    NX_Error err;
    NX_Process * process;
    NX_ExposedObject * exobj;
    
    if (solt == NX_SOLT_INVALID_VALUE)
    {
        return NX_EINVAL;
    }

    process = NX_ProcessCurrent();
    if ((exobj = NX_ProcessGetSolt(process, solt)) == NX_NULL)
    {
        return NX_ENOSRCH;
    }

    if (exobj->type != NX_EXOBJ_SEMAPHORE)
    {
        return NX_ENORES;
    }

    sem = (NX_Semaphore *)exobj->object;
    NX_ASSERT(sem);

    if ((err = NX_SemaphoreSignalAll(sem)) != NX_EOK)
    {
        return err;
    }

    return NX_EOK;
}

NX_PRIVATE NX_Error SysSemaphoreGetValue(NX_Solt solt, NX_IArch * outValue)
{
    NX_Semaphore * sem;
    NX_Process * process;
    NX_ExposedObject * exobj;
    NX_IArch value;
    
    if (solt == NX_SOLT_INVALID_VALUE || !outValue)
    {
        return NX_EINVAL;
    }

    process = NX_ProcessCurrent();
    if ((exobj = NX_ProcessGetSolt(process, solt)) == NX_NULL)
    {
        return NX_ENOSRCH;
    }

    if (exobj->type != NX_EXOBJ_SEMAPHORE)
    {
        return NX_ENORES;
    }

    sem = (NX_Semaphore *)exobj->object;
    NX_ASSERT(sem);

    value = NX_SemaphoreGetValue(sem);

    NX_CopyToUser((char *)outValue, (char *)&value, sizeof(value));

    return NX_EOK;
}

NX_PRIVATE NX_Error MutexCloseSolt(void * object, NX_ExposedObjectType type)
{
    NX_Mutex * mutex;

    if (type != NX_EXOBJ_MUTEX)
    {
        return NX_ENORES;
    }

    mutex = (NX_Mutex *) object;
    NX_ASSERT(mutex);

    if (NX_MutexIsLocked(mutex) == NX_True)
    {
        return NX_ERROR;
    }

    NX_MemZero(mutex, sizeof(mutex));
    NX_MemFree(mutex);

    return NX_EOK;
}

#define NX_MUTEX_ATTR_LOCKED 0x01

NX_PRIVATE NX_Error SysMutexCreate(NX_U32 attr, NX_Solt * outSolt)
{
    NX_Mutex * mutex;
    NX_Error err;
    NX_Solt solt = NX_SOLT_INVALID_VALUE;
    NX_Process * process;
    
    if (!outSolt)
    {
        return NX_EINVAL;
    }

    mutex = NX_MemAlloc(sizeof(NX_Mutex));
    if (mutex == NX_NULL)
    {
        return NX_ENOMEM;
    }

    if (attr & NX_MUTEX_ATTR_LOCKED)
    {
        if ((err = NX_MutexInitLocked(mutex)) != NX_EOK)
        {
            NX_MemFree(mutex);
            return err;
        }
    }
    else
    {
        if ((err = NX_MutexInit(mutex)) != NX_EOK)
        {
            NX_MemFree(mutex);
            return err;
        }
    }

    process = NX_ProcessCurrent();
    if ((err = NX_ProcessInstallSolt(process, mutex, NX_EXOBJ_MUTEX, MutexCloseSolt, &solt)) != NX_EOK)
    {
        NX_MemFree(mutex);
        return err;
    }

    NX_CopyToUser((char *)outSolt, (char *)&solt, sizeof(solt));

    return NX_EOK;
}

NX_PRIVATE NX_Error SysMutexDestroy(NX_Solt solt)
{
    NX_Mutex * mutex;
    NX_Error err;
    NX_Process * process;
    NX_ExposedObject * exobj;
    
    if (solt == NX_SOLT_INVALID_VALUE)
    {
        return NX_EINVAL;
    }

    process = NX_ProcessCurrent();
    if ((exobj = NX_ProcessGetSolt(process, solt)) == NX_NULL)
    {
        return NX_ENOSRCH;
    }

    if (exobj->type != NX_EXOBJ_MUTEX)
    {
        return NX_ENORES;
    }

    mutex = (NX_Mutex *)exobj->object;
    NX_ASSERT(mutex);

   if (NX_MutexIsLocked(mutex) == NX_True)
    {
        return NX_ERROR;
    }

    if ((err = NX_ProcessUninstallSolt(process, solt)) != NX_EOK)
    {
        return err;
    }

    NX_MemZero(mutex, sizeof(mutex));
    NX_MemFree(mutex);

    return NX_EOK;
}

NX_PRIVATE NX_Error SysMutexAcquire(NX_Solt solt)
{
    NX_Mutex * mutex;
    NX_Error err;
    NX_Process * process;
    NX_ExposedObject * exobj;
    
    if (solt == NX_SOLT_INVALID_VALUE)
    {
        return NX_EINVAL;
    }

    process = NX_ProcessCurrent();
    if ((exobj = NX_ProcessGetSolt(process, solt)) == NX_NULL)
    {
        return NX_ENOSRCH;
    }

    if (exobj->type != NX_EXOBJ_MUTEX)
    {
        return NX_ENORES;
    }

    mutex = (NX_Mutex *)exobj->object;
    NX_ASSERT(mutex);

    if ((err = NX_MutexLock(mutex)) != NX_EOK)
    {
        return err;
    }

    return NX_EOK;
}

NX_PRIVATE NX_Error SysMutexTryAcquire(NX_Solt solt)
{
    NX_Mutex * mutex;
    NX_Error err;
    NX_Process * process;
    NX_ExposedObject * exobj;
    
    if (solt == NX_SOLT_INVALID_VALUE)
    {
        return NX_EINVAL;
    }

    process = NX_ProcessCurrent();
    if ((exobj = NX_ProcessGetSolt(process, solt)) == NX_NULL)
    {
        return NX_ENOSRCH;
    }

    if (exobj->type != NX_EXOBJ_MUTEX)
    {
        return NX_ENORES;
    }

    mutex = (NX_Mutex *)exobj->object;
    NX_ASSERT(mutex);

    if ((err = NX_MutexTryLock(mutex)) != NX_EOK)
    {
        return err;
    }

    return NX_EOK;
}

NX_PRIVATE NX_Error SysMutexRelease(NX_Solt solt)
{
    NX_Mutex * mutex;
    NX_Error err;
    NX_Process * process;
    NX_ExposedObject * exobj;
    
    if (solt == NX_SOLT_INVALID_VALUE)
    {
        return NX_EINVAL;
    }

    process = NX_ProcessCurrent();
    if ((exobj = NX_ProcessGetSolt(process, solt)) == NX_NULL)
    {
        return NX_ENOSRCH;
    }

    if (exobj->type != NX_EXOBJ_MUTEX)
    {
        return NX_ENORES;
    }

    mutex = (NX_Mutex *)exobj->object;
    NX_ASSERT(mutex);

    if ((err = NX_MutexUnlock(mutex)) != NX_EOK)
    {
        return err;
    }

    return NX_EOK;
}

NX_PRIVATE NX_Error SysMutexAcquirable(NX_Solt solt)
{
    NX_Mutex * mutex;
    NX_Process * process;
    NX_ExposedObject * exobj;
    
    if (solt == NX_SOLT_INVALID_VALUE)
    {
        return NX_EINVAL;
    }

    process = NX_ProcessCurrent();
    if ((exobj = NX_ProcessGetSolt(process, solt)) == NX_NULL)
    {
        return NX_ENOSRCH;
    }

    if (exobj->type != NX_EXOBJ_MUTEX)
    {
        return NX_ENORES;
    }

    mutex = (NX_Mutex *)exobj->object;
    NX_ASSERT(mutex);

    if (NX_MutexIsLocked(mutex) == NX_True)
    {
        return NX_ERROR;
    }

    return NX_EOK;
}

NX_PRIVATE NX_Error DeviceCloseSolt(void * object, NX_ExposedObjectType type)
{
    NX_Device * dev;

    if (type != NX_EXOBJ_DEVICE)
    {
        return NX_ENORES;
    }

    dev = (NX_Device *) object;
    NX_ASSERT(dev);

    return NX_DeviceClose(dev);
}

NX_PRIVATE NX_Error SysDeviceOpen(const char * name, NX_U32 flags, NX_Solt * outSolt)
{
    NX_Device * dev = NX_NULL;
    NX_Error err;
    NX_Solt solt = NX_SOLT_INVALID_VALUE;
    NX_Process * process;
    
    if (!name || !outSolt)
    {
        return NX_EINVAL;
    }

    err = NX_DeviceOpen(name, flags, &dev);
    if (err != NX_EOK)
    {
        return err;
    }

    process = NX_ProcessCurrent();
    if ((err = NX_ProcessInstallSolt(process, dev, NX_EXOBJ_DEVICE, DeviceCloseSolt, &solt)) != NX_EOK)
    {
        NX_DeviceClose(dev);
        return err;
    }

    NX_CopyToUser((char *)outSolt, (char *)&solt, sizeof(solt));

    return NX_EOK;
}

NX_PRIVATE NX_Error SysDeviceRead(NX_Solt solt, void *buf, NX_Offset off, NX_Size len, NX_Size *outLen)
{
    NX_Device * dev = NX_NULL;
    NX_Error err;
    NX_Process * process;
    NX_ExposedObject * exobj;
    
    if (solt == NX_SOLT_INVALID_VALUE)
    {
        return NX_EINVAL;
    }

    process = NX_ProcessCurrent();
    if ((exobj = NX_ProcessGetSolt(process, solt)) == NX_NULL)
    {
        return NX_ENOSRCH;
    }

    if (exobj->type != NX_EXOBJ_DEVICE)
    {
        return NX_ENORES;
    }

    dev = (NX_Device *)exobj->object;
    NX_ASSERT(dev);

    if ((err = NX_DeviceRead(dev, buf, off, len, outLen)) != NX_EOK)
    {
        return err;
    }

    return NX_EOK;
}

NX_PRIVATE NX_Error SysDeviceWrite(NX_Solt solt, void *buf, NX_Offset off, NX_Size len, NX_Size *outLen)
{
    NX_Device * dev = NX_NULL;
    NX_Error err;
    NX_Process * process;
    NX_ExposedObject * exobj;
    
    if (solt == NX_SOLT_INVALID_VALUE)
    {
        return NX_EINVAL;
    }

    process = NX_ProcessCurrent();
    if ((exobj = NX_ProcessGetSolt(process, solt)) == NX_NULL)
    {
        return NX_ENOSRCH;
    }

    if (exobj->type != NX_EXOBJ_DEVICE)
    {
        return NX_ENORES;
    }

    dev = (NX_Device *)exobj->object;
    NX_ASSERT(dev);

    if ((err = NX_DeviceWrite(dev, buf, off, len, outLen)) != NX_EOK)
    {
        return err;
    }

    return NX_EOK;
}

NX_PRIVATE NX_Error SysDeviceControl(NX_Solt solt, NX_U32 cmd, void *arg)
{
    NX_Device * dev = NX_NULL;
    NX_Error err;
    NX_Process * process;
    NX_ExposedObject * exobj;
    
    if (solt == NX_SOLT_INVALID_VALUE)
    {
        return NX_EINVAL;
    }

    process = NX_ProcessCurrent();
    if ((exobj = NX_ProcessGetSolt(process, solt)) == NX_NULL)
    {
        return NX_ENOSRCH;
    }

    if (exobj->type != NX_EXOBJ_DEVICE)
    {
        return NX_ENORES;
    }

    dev = (NX_Device *)exobj->object;
    NX_ASSERT(dev);

    if ((err = NX_DeviceControl(dev, cmd, arg)) != NX_EOK)
    {
        return err;
    }

    return NX_EOK;
}

NX_PRIVATE NX_Error SysDeviceMap(NX_Solt solt, NX_Size length, NX_U32 prot, void ** outAddr)
{
    NX_Device * dev = NX_NULL;
    NX_Error err;
    NX_Process * process;
    NX_ExposedObject * exobj;
    NX_Addr phyAddr = 0;
    NX_UArch attr;
    void * mapAddr = NX_NULL;
    
    if (solt == NX_SOLT_INVALID_VALUE || !length || !prot || outAddr == NX_NULL)
    {
        return NX_EINVAL;
    }

    process = NX_ProcessCurrent();
    if ((exobj = NX_ProcessGetSolt(process, solt)) == NX_NULL)
    {
        return NX_ENOSRCH;
    }

    if (exobj->type != NX_EXOBJ_DEVICE)
    {
        return NX_ENORES;
    }

    dev = (NX_Device *)exobj->object;
    NX_ASSERT(dev);

    if ((err = NX_DeviceMappable(dev, length, prot, &phyAddr)) != NX_EOK)
    {
        return err;
    }

    if (!phyAddr)
    {
        return NX_EPERM;
    }

    /* make attr */
    attr = NX_PAGE_ATTR_USER & (~NX_PAGE_ATTR_RWX);
    if (prot & NX_PROT_READ)
    {
        attr |= NX_PAGE_ATTR_READ;
    }
    if (prot & NX_PROT_WRITE)
    {
        attr |= NX_PAGE_ATTR_WRITE;
    }
    if (prot & NX_PROT_EXEC)
    {
        attr |= NX_PAGE_ATTR_EXEC;
    }

    if ((err = NX_VmspaceMapWithPhy(&process->vmspace, 0, phyAddr, length, attr, 0, &mapAddr)) != NX_EOK)
    {
        return err;
    }

    NX_CopyToUser((char *)outAddr, (char *)&mapAddr, sizeof(void *));

    return NX_EOK;
}

NX_Error SysSignalSend(NX_U32 tid, NX_Signal signal, void * signalValue)
{
    NX_Thread * thread;
    NX_Process * process;
    
    if ((thread = NX_ThreadFindById(tid)) == NX_NULL)
    {
        return NX_ENOSRCH;
    }

    if ((process = NX_ThreadGetProcess(thread)) == NX_NULL) /* no process */
    {
        return NX_EPERM;
    }

    return NX_SignalSend(tid, signal, signalValue, NX_NULL);
}

NX_Error SysSignalGetAttr(NX_Signal signal, NX_SignalAttr * outAttr)
{
    return NX_SignalGetAttr(signal, outAttr);
}

NX_Error SysSignalSetAttr(NX_Signal signal, NX_SignalAttr * attr)
{
    return NX_SignalSetAttr(signal, attr);
}

NX_Error SysSignalContorl(NX_Signal signalFirst, NX_Signal signalLast, NX_U32 cmd)
{
    return NX_SignalControl(signalFirst, signalLast, cmd);
}

NX_PRIVATE NX_Error TimerCloseSolt(void * object, NX_ExposedObjectType type)
{
    NX_Timer * timer = NX_NULL;

    if (type != NX_EXOBJ_TIMER)
    {
        return NX_ENORES;
    }

    timer = (NX_Timer *) object;
    NX_ASSERT(timer);

    NX_TimerStop(timer); /* stop timer before destory */
    return NX_TimerDestroy(timer);
}

NX_PRIVATE NX_Bool SysTimerHandler(NX_Timer * timer, void * arg)
{
    NX_SignalExtralInfo infoExtral;

    infoExtral.timerHandler = (NX_SignalTimerHandler)NX_TimerGetExtension(timer);
    infoExtral.timerArg = arg;

    NX_SignalSend(NX_TimerGetTid(timer), NX_SIGNAL_TIMER, NX_NULL, &infoExtral);
    return NX_True;
}

NX_PRIVATE NX_Error SysTimerCreate(NX_UArch milliseconds, void (*handler)(void *arg), void *arg, NX_U32 flags, NX_Solt * outSolt)
{
    NX_Timer * timer = NX_NULL;
    NX_Error err;
    NX_Solt solt = NX_SOLT_INVALID_VALUE;
    NX_Process * process;
    NX_Thread * self;
    
    if (!milliseconds || !outSolt)
    {
        return NX_EINVAL;
    }

    timer = NX_TimerCreate(milliseconds, SysTimerHandler, arg, flags);
    if (timer == NX_NULL)
    {
        return NX_ENOMEM;
    }

    self = NX_ThreadSelf();
    process = NX_ThreadGetProcess(self);
    if ((err = NX_ProcessInstallSolt(process, timer, NX_EXOBJ_TIMER, TimerCloseSolt, &solt)) != NX_EOK)
    {
        NX_TimerDestroy(timer);
        return err;
    }

    NX_TimerSetExtension(timer, handler);
    NX_TimerSetTid(timer, self->tid);

    NX_CopyToUser((char *)outSolt, (char *)&solt, sizeof(solt));

    return NX_EOK;
}

NX_PRIVATE NX_Error SysTimerStart(NX_Solt solt)
{
    NX_Timer * timer = NX_NULL;
    NX_Error err;
    NX_Process * process;
    NX_ExposedObject * exobj;
    
    if (solt == NX_SOLT_INVALID_VALUE)
    {
        return NX_EINVAL;
    }

    process = NX_ProcessCurrent();
    if ((exobj = NX_ProcessGetSolt(process, solt)) == NX_NULL)
    {
        return NX_ENOSRCH;
    }

    if (exobj->type != NX_EXOBJ_TIMER)
    {
        return NX_ENORES;
    }

    timer = (NX_Timer *)exobj->object;
    NX_ASSERT(timer);

    if ((err = NX_TimerStart(timer)) != NX_EOK)
    {
        return err;
    }

    return NX_EOK;
}

NX_PRIVATE NX_Error SysTimerStop(NX_Solt solt)
{
    NX_Timer * timer = NX_NULL;
    NX_Error err;
    NX_Process * process;
    NX_ExposedObject * exobj;
    
    if (solt == NX_SOLT_INVALID_VALUE)
    {
        return NX_EINVAL;
    }

    process = NX_ProcessCurrent();
    if ((exobj = NX_ProcessGetSolt(process, solt)) == NX_NULL)
    {
        return NX_ENOSRCH;
    }

    if (exobj->type != NX_EXOBJ_TIMER)
    {
        return NX_ENORES;
    }

    timer = (NX_Timer *)exobj->object;
    NX_ASSERT(timer);

    if ((err = NX_TimerStop(timer)) != NX_EOK)
    {
        return err;
    }

    return NX_EOK;
}

NX_Error SysDriverRegister(NX_UserDriver * drv, NX_Solt * outSolt)
{
    return NX_UserDriverRegister(drv, outSolt);
}

void SysThreadYield(void)
{
    return NX_ThreadYield();
}

#define __NX_POLL_SOLT_TABLE_NR 32

NX_PRIVATE void ReportPollState(NX_PollState * pState, NX_Solt * solt)
{
    NX_PollState kPollState;
    NX_U32 inmask, outmask;

    NX_CopyFromUser((char *)&kPollState, (char *)pState, sizeof(NX_PollState));

    /* copy solt */
    inmask = kPollState.inmask;
    outmask = kPollState.outmask;
    outmask &= inmask; /* only poll inmask events */

    NX_CopyToUser((char *)&pState->solt, (char *)solt, sizeof(NX_Solt));                
    NX_CopyToUser((char *)&pState->outmask, (char *)&outmask, sizeof(NX_U32));
}

/**
 * poll wait sleep for each time, N ms
 */
#define NX_POLL_WAIT_SLEEP_EACH_TIME 3

/**
 * poll solt
 * 
 * @param soltTable: poll table
 * @param maxSoltCount: poll solt count
 * @param timeout: poll timeout, -1 means block wait. or not wait timeout ms then return
 * @param epState: poll state
 * 
 * @return poll success return NX_EOK, device no poll support return NX_ENOFUNC, 
 *         if no data return NX_ENORES, or not return others
 */
NX_Error SysPollWait(NX_Solt * soltTable, NX_Size maxSoltCount, int timeout, NX_PollState * pState)
{
    NX_Solt staticSoltTable[__NX_POLL_SOLT_TABLE_NR] = {0};
    NX_Solt * kSoltTable = staticSoltTable; /* kernel solt table */
    int i;
    NX_Process * process;
    NX_ExposedObject * exobj;
    NX_Error err;
    NX_Bool waitForever;
    NX_TimeVal startTimeout;
    NX_TimeVal deltaTimeout;

    if (!soltTable || !maxSoltCount || !pState)
    {
        return NX_EINVAL;
    }

    if (maxSoltCount > __NX_POLL_SOLT_TABLE_NR)
    {
        if ((kSoltTable = NX_MemAlloc(maxSoltCount * sizeof(NX_Solt))) == NX_NULL)
        {
            return NX_ENOMEM;
        }
    }

    NX_CopyFromUser((char *)kSoltTable, (char *)soltTable, maxSoltCount * sizeof(NX_Solt));

    process = NX_ProcessCurrent();

    /* check solt invalid */
    for (i = 0; i < maxSoltCount; i++)
    {
        if (kSoltTable[i] < 0)
        {
            continue;
        }
        if ((exobj = NX_ProcessGetSolt(process, kSoltTable[i])) == NX_NULL)
        {
            err = NX_EPERM;
            goto out;
        }
        if (exobj->type != NX_EXOBJ_DEVICE && exobj->type != NX_EXOBJ_FIFO)
        {
            err = NX_EPERM;
            goto out;
        }
    }
    
    /* calc timeout */
    if (timeout < 0) /* poll wait */
    {
        waitForever = NX_True;
    }
    else
    {
        waitForever = NX_False;
    }
    startTimeout = NX_ClockGetMillisecond();
    deltaTimeout = timeout;

    for (;;)
    {
        err = NX_EOK;
        for (i = 0; i < maxSoltCount; i++)
        {
            if (kSoltTable[i] < 0)
            {
                continue;
            }
            if ((exobj = NX_ProcessGetSolt(process, kSoltTable[i])) == NX_NULL)
            {
                err = NX_EPERM;
                goto out;
            }
            if (exobj->type == NX_EXOBJ_DEVICE)
            {
                NX_Device * dev = (NX_Device *)exobj->object;

                if ((err = NX_DevicePoll(dev, pState)) == NX_EOK)
                {
                    ReportPollState(pState, &kSoltTable[i]);
                    goto out;
                }
            }
            else if (exobj->type == NX_EXOBJ_FIFO)
            {
                NX_Fifo * fifo = (NX_Fifo *)exobj->object;

                if ((err = NX_FifoPoll(fifo, pState)) == NX_EOK)
                {
                    ReportPollState(pState, &kSoltTable[i]);
                    goto out;
                }
                
            }
            else
            {
                err = NX_EPERM;
                goto out;
            }
            
            if (err != NX_ENORES) /* if no data, must return ENORES */
            {
                goto out;
            }
        }

        /* check signal */
        if (NX_SignalCurrentPending())
        {
            err = NX_EINTR;
            break;
        }

        /* check timeout */
        if (!waitForever)
        {
            if (NX_ClockGetMillisecond() > (startTimeout + deltaTimeout)) /* timeout */
            {
                err = NX_EINTR;
                break;
            }
        }

        NX_ThreadSleep(NX_POLL_WAIT_SLEEP_EACH_TIME);
    }

out:
    if (maxSoltCount > __NX_POLL_SOLT_TABLE_NR)
    {
        NX_MemFree(kSoltTable);
    }
    return err;
}

NX_Error ShmCloseSolt(void * object, NX_ExposedObjectType type)
{
    NX_ShareMem * shm;

    if (type != NX_EXOBJ_SHM)
    {
        return NX_ENORES;
    }

    shm = (NX_ShareMem *) object;
    NX_ASSERT(shm);

    return NX_ShareMemClose(shm);
}

NX_Error SysShareMemOpen(const char * name, NX_Size size, NX_U32 flags, NX_Solt * outSolt)
{
    NX_Process * process;
    NX_Error err;
    NX_Solt solt = NX_SOLT_INVALID_VALUE;
    NX_ShareMem * shm = NX_ShareMemOpen(name, size, flags);
    if (!shm)
    {
        return NX_ENOMEM;
    }

    process = NX_ProcessCurrent();
    if ((err = NX_ProcessInstallSolt(process, shm, NX_EXOBJ_SHM, ShmCloseSolt, &solt)) != NX_EOK)
    {
        NX_ShareMemClose(shm);
        return err;
    }

    NX_CopyToUser((char *)outSolt, (char *)&solt, sizeof(solt));

    return err;
}

NX_PRIVATE NX_Error ShmAddrCloseSolt(void * object, NX_ExposedObjectType type)
{
    NX_ShareAddr * shareAddr;
    NX_Error err;

    if (type != NX_EXOBJ_SHMADDR)
    {
        return NX_ENORES;
    }

    shareAddr = (NX_ShareAddr *) object;
    NX_ASSERT(shareAddr);

    err = NX_ShareMemUnmap(shareAddr->shm, shareAddr->vaddr);
    if (err != NX_EOK)
    {
        NX_LOG_E("unmap shm %p vaddr %p faield!", shareAddr->shm, shareAddr->vaddr);
        return err;
    }
    NX_MemFree(shareAddr);
    return NX_EOK;
}

NX_Error SysShareMemMap(NX_Solt shmSolt, void ** outMapAddr, NX_Solt * outSolt)
{
    NX_Process * process;
    NX_ShareMem * shm;
    NX_ExposedObject * exobj;
    void * mapAddr = NX_NULL;
    NX_Error err = NX_EOK;
    NX_Solt solt = NX_SOLT_INVALID_VALUE;
    NX_ShareAddr * shareAddr;

    if (shmSolt < 0 || !outMapAddr || !outSolt)
    {
        return NX_EINVAL;
    }

    /* copy null and invalid solt */
    NX_CopyToUser((char *)outMapAddr, (char *)&mapAddr, sizeof(mapAddr));
    NX_CopyToUser((char *)outSolt, (char *)&solt, sizeof(solt));

    process = NX_ProcessCurrent();

    exobj = NX_ProcessGetSolt(process, shmSolt);
    if (!exobj)
    {
        return NX_EINVAL;
    }

    if (exobj->type != NX_EXOBJ_SHM)
    {
        return NX_EPERM;
    }

    shm = (NX_ShareMem *) exobj->object;
    NX_ASSERT(shm);

    shareAddr = NX_MemAlloc(sizeof(NX_ShareAddr));
    if (!shareAddr)
    {
        return NX_ENOMEM;
    }

    if ((err = NX_ShareMemMap(shm, &mapAddr)) != NX_EOK)
    {
        NX_MemFree(shareAddr);
        return err;
    }

    shareAddr->shm = shm;
    shareAddr->vaddr = mapAddr;

    /* install addr for unmap sharemem */
    if ((err = NX_ProcessInstallSolt(process, shareAddr, NX_EXOBJ_SHMADDR, ShmAddrCloseSolt, &solt)) != NX_EOK)
    {
        NX_MemFree(shareAddr);
        NX_ShareMemUnmap(shm, mapAddr);
        return err;
    }

    NX_CopyToUser((char *)outMapAddr, (char *)&mapAddr, sizeof(mapAddr));
    NX_CopyToUser((char *)outSolt, (char *)&solt, sizeof(solt));

    return err;
}

NX_PRIVATE NX_Error IpcCreateHelperThread(NX_Thread * server, NX_IpcChannel * channel,
    void * handler, void * arg)
{
    NX_Thread * thread;
    NX_Thread * client;
    NX_Process * process;
    NX_Error err;
    void * stackBase = NX_NULL;
    char name[32];

    client = channel->clientThread;

    NX_SNPrintf(name, 32, NX_IPC_HELPER_NAME "/%s:%d", client->name, client->tid);

    /* create helper thread with server fixed priority */
    thread = NX_ThreadCreate(name, NX_UserThreadEntry, NX_NULL, server->fixedPriority);
    if (!thread)
    {
        NX_LOG_E("create ipc server thread failed!");
        return NX_EINVAL;
    }

    process = NX_ThreadGetProcess(server);
    NX_ASSERT(process);

    /* map stack */
    err = NX_VmspaceMap(&process->vmspace, 0, NX_IPC_THREAD_STACK_SIZE, NX_PAGE_ATTR_USER, 0, &stackBase);
    if (err != NX_EOK)
    {
        NX_LOG_E("map user stack error!");
        return err;
    }
    NX_ASSERT(stackBase);

    /* set user info */
    thread->userHandler = handler;
    thread->userStackSize = NX_IPC_THREAD_STACK_SIZE;
    thread->userStackBase = stackBase;
    thread->userArg = arg;

    /* map tls */
    if (NX_ProcessMapTls(process, thread) != NX_EOK)
    {
        NX_LOG_E("map thread tls error!");
        NX_ThreadDestroy(thread);
        NX_VmspaceUnmap(&process->vmspace, (NX_Addr)stackBase, NX_IPC_THREAD_STACK_SIZE, 0);
        return NX_ENOMEM;
    }

    /* add thread to system */
    NX_ProcessAppendThread(process, thread);
    NX_ThreadEnququeGlobalListUnlocked(thread);

    channel->serverThread = thread; /* update server as new server thread */

    thread->resource.ipcChannel = channel;
    
    return NX_EOK;
}

NX_PRIVATE NX_Error IpcDestroyHelperThread(NX_IpcChannel * channel)
{
    NX_Thread * thread;
    NX_Process * process;

    if (!channel)
    {
        return NX_EINVAL;
    }

    thread = channel->serverThread;
    NX_ASSERT(thread);
    process = NX_ThreadGetProcess(thread);

    NX_ThreadDeququeGlobalListUnlocked(thread);
    NX_ProcessDeleteThread(process, thread);
    NX_ProcessUnmapTls(process, thread);
    NX_VmspaceUnmap(&process->vmspace, (NX_Addr)thread->userStackBase, NX_IPC_THREAD_STACK_SIZE, 0);
    NX_ThreadDestroy(thread);
    return NX_EOK;
}

NX_PRIVATE NX_Error IpcCreateShm(NX_Thread * client, NX_Thread * server, NX_Size bufSize,
    NX_Addr * outClientBufAddr, NX_Addr * outServerBufAddr,
    NX_ShareMem ** outShm)
{
    NX_Addr clientBufAddr;
    NX_Addr serverBufAddr;

    /* create share memory */
    NX_ShareMem * shm = NX_ShareMemoryCreate(NX_NULL, bufSize, NX_SHAREMEM_ANONYMOUS);
    if (!shm)
    {
        return NX_ENOMEM;
    }
    
    /* map shm to user space */
    if (NX_ShareMemMapToVmspace(shm, &NX_ThreadGetProcess(server)->vmspace, (void **)&serverBufAddr) != NX_EOK)
    {
        NX_ShareMemClose(shm);
        return NX_ENOMEM;
    }

    if (NX_ShareMemMapToVmspace(shm, &NX_ThreadGetProcess(client)->vmspace, (void **)&clientBufAddr) != NX_EOK)
    {
        NX_ShareMemUnmapFromVmspace(shm, &NX_ThreadGetProcess(server)->vmspace, (void *)serverBufAddr);
        NX_ShareMemClose(shm);
        return NX_ENOMEM;
    }

    *outClientBufAddr = clientBufAddr;
    *outServerBufAddr = serverBufAddr;
    *outShm = shm;

    return NX_EOK;
}

NX_PRIVATE void IpcDestroyShm(NX_Vmspace * clientSpcae, NX_Vmspace * serverSpace, NX_ShareMem * shm,
    NX_Addr clientBufAddr, NX_Addr serverBufAddr)
{
    NX_ShareMemUnmapFromVmspace(shm, clientSpcae, (void *)clientBufAddr);
    NX_ShareMemUnmapFromVmspace(shm, serverSpace, (void *)serverBufAddr);
    NX_ShareMemClose(shm);
}

NX_PRIVATE NX_Error IpcCreateClient(NX_Thread * client, NX_Thread * server,
    NX_IpcClientConfig * outConfig, NX_Size bufSize,
    NX_IpcChannel ** outConnect)
{
    NX_IpcServer * ipc;
    NX_Addr serverBufAddr;
    NX_Addr clientBufAddr;
    NX_Error err;
    NX_IpcChannel * channel;
    NX_ShareMem * shm = NX_NULL;

    ipc = server->resource.ipcServer;
    
    if ((err = IpcCreateShm(client, server, 
        bufSize, &clientBufAddr, &serverBufAddr, &shm)) != NX_EOK)
    {
        return err;
    }

    channel = NX_IpcCreateChannel(ipc, serverBufAddr, clientBufAddr, bufSize);
    if (!channel)
    {
        IpcDestroyShm(&NX_ThreadGetProcess(client)->vmspace, &NX_ThreadGetProcess(server)->vmspace,
            shm, clientBufAddr, serverBufAddr);
        return NX_ENOMEM;
    }

    /* add thread info and shm */
    channel->clientThread = client;
    channel->shm = shm;
    /* create server thread */
    if ((err = IpcCreateHelperThread(server, channel, ipc->handler, (void *)serverBufAddr)) != NX_EOK)
    {
        NX_IpcDestroyChannel(channel);
        IpcDestroyShm(&NX_ThreadGetProcess(client)->vmspace, &NX_ThreadGetProcess(server)->vmspace,
            shm, clientBufAddr, serverBufAddr);
        return err;
    }

    NX_AtomicInc(&ipc->channelCount);

    outConfig->bufAddr = channel->buf.clientBufAddr;
    outConfig->bufSize = bufSize;
    
    *outConnect = channel;

    return NX_EOK;
}

NX_PRIVATE NX_Error IpcDestroyClient(NX_IpcChannel * channel)
{
    NX_ShareMem * shm;
    NX_Addr clientBufAddr;
    NX_Addr serverBufAddr;
    NX_Process * clientProc;
    NX_Process * serverProc;
    NX_Thread * server;
    
    shm = channel->shm;
    server = channel->serverThread;

    clientProc = NX_ThreadGetProcess((NX_Thread *)channel->clientThread);
    /* get process from ipc process, but not server thread */
    serverProc = channel->ipc->process;
    
    clientBufAddr = channel->buf.clientBufAddr;
    serverBufAddr = channel->buf.serverBufAddr;

    /* terminate user thread */
    if (channel->state != NX_IPC_CHANNEL_INIT)
    {
        while (channel->state != NX_IPC_CHANNEL_CLOSE)
        {
            NX_SignalSend(server->tid, NX_SIGNAL_KILL, NX_NULL, NX_NULL);
            NX_ThreadSleep(50);
        }
    }
    else
    {
        IpcDestroyHelperThread(channel);
    }

    IpcDestroyShm(&clientProc->vmspace, &serverProc->vmspace,
        shm, clientBufAddr, serverBufAddr);
    NX_IpcDestroyChannel(channel);

    return NX_EOK;
}

NX_PRIVATE NX_Error IpcChannelCloseSolt(void * object, NX_ExposedObjectType type)
{
    NX_IpcChannel * channel;
    NX_IpcServer * ipc;

    if (type != NX_EXOBJ_IPC_CHANNEL)
    {
        return NX_ENORES;
    }

    channel = (NX_IpcChannel *) object;
    NX_ASSERT(channel);
    ipc = channel->ipc;

    IpcDestroyClient(channel);
    NX_AtomicDec(&ipc->channelCount);
    
    return NX_EOK;
}

NX_PRIVATE NX_Error SysIpcConnect(const char * name, NX_IpcClientConfig * outConfig, NX_Size bufSize, NX_Solt * outSolt)
{
    NX_IpcServer * ipc;
    NX_Thread * client, * server;
    NX_IpcClientConfig config;
    NX_Error err;
    NX_IpcChannel * channel = NX_NULL;
    NX_Solt solt = NX_SOLT_INVALID_VALUE;
    NX_Solt clientSolt = NX_SOLT_INVALID_VALUE;

    if (!name || !outConfig || !bufSize || !outSolt)
    {
        return NX_EINVAL;
    }

    ipc = NX_IpcSearch(name);
    if (!ipc)
    {
        return NX_ENOSRCH;
    }

    if (NX_AtomicGet(&ipc->channelCount) >= ipc->maxClient)
    {
        return NX_ENORES;
    }

    client = NX_ThreadSelf();
    server = ipc->thread;

    if ((err = IpcCreateClient(client, server, &config, bufSize, &channel)) != NX_EOK)
    {
        return err;
    }

    /* install channel */
    if ((err = NX_ProcessInstallSolt(NX_ThreadGetProcess(client), channel,
        NX_EXOBJ_IPC_CHANNEL, IpcChannelCloseSolt, &solt)) != NX_EOK)
    {
        IpcDestroyClient(channel);
        return err;
    }
    
    /* install client thread into server process */
    if ((err = NX_ProcessInstallSolt(NX_ThreadGetProcess((NX_Thread *)channel->serverThread), client,
        NX_EXOBJ_THREAD, NX_NULL, &clientSolt)) != NX_EOK)
    {
        IpcDestroyClient(channel);
        return err;
    }

    /* used for ipc copy solt */
    channel->clientSolt = clientSolt;

    NX_CopyToUser((char *)outConfig, (char *)&config, sizeof(NX_IpcClientConfig));
    NX_CopyToUser((char *)outSolt, (char *)&solt, sizeof(solt));

    return NX_EOK;
}

NX_PRIVATE NX_Error IpcCloseSolt(void * object, NX_ExposedObjectType type)
{
    NX_IpcServer * ipc;

    if (type != NX_EXOBJ_IPC)
    {
        return NX_ENORES;
    }

    ipc = (NX_IpcServer *) object;
    NX_ASSERT(ipc);

    return NX_IpcDestroyServer(ipc);
}

NX_PRIVATE NX_Error SysIpcBind(const char * name, void * serverHandler, NX_Size maxClient, NX_Solt * outSolt)
{
    NX_Thread * cur;
    NX_Error err;
    NX_Solt solt;
    NX_IpcServer * ipc;

    if (!name || !serverHandler || !maxClient || !outSolt)
    {
        return NX_EINVAL;
    }

    if (NX_StrLen(name) > NX_IPC_NAME_LEN || maxClient >= NX_IPC_CHANNEL_MAX)
    {
        return NX_EINVAL;
    }

    if (NX_IpcSearch(name) != NX_NULL)
    {
        return NX_EBUSY;
    }

    cur = NX_ThreadSelf();

    if (cur->resource.ipcServer != NX_NULL)
    {
        return NX_EBUSY;
    }

    ipc = NX_IpcCreateServer(name, serverHandler, maxClient);
    if (!ipc)
    {
        return NX_ENOMEM;
    }

    ipc->thread = cur;
    ipc->process = NX_ThreadGetProcess(cur);

    if ((err = NX_ProcessInstallSolt(NX_ThreadGetProcess(cur), 
        ipc, NX_EXOBJ_IPC, IpcCloseSolt, &solt)) != NX_EOK)
    {
        NX_IpcDestroyServer(ipc);
        return err;
    }

    cur->resource.ipcServer = ipc;

    /* after bind, raise priority to HIGH */
    NX_ThreadChangePriority(cur, NX_THREAD_PRIORITY_HIGH);

    NX_CopyToUser((char *)outSolt, (char *)&solt, sizeof(solt));

    return NX_EOK;
}

NX_PRIVATE NX_Solt *IpcSendCap(NX_IpcChannel * channel, NX_IpcMsg * msg, NX_Size *outSlotCount)
{
    NX_Size slotCount;
    NX_Size capSlotsOffset;
    NX_Error err;

    NX_CopyFromUser((char *)&slotCount, (char *)&msg->soltCount, sizeof(slotCount));
    NX_CopyFromUser((char *)&capSlotsOffset, (char *)&msg->capSlotsOffset, sizeof(capSlotsOffset));

    if (slotCount == 0)
        return NX_NULL;

    NX_Solt *soltBuf = NX_MemAlloc(slotCount * sizeof(NX_Solt));
    NX_CopyFromUser((char *)soltBuf, (char *)msg + capSlotsOffset, sizeof(NX_Solt) * slotCount);

    for (int i = 0; i < slotCount; i++)
    {
        if (soltBuf[i] == NX_SOLT_INVALID_VALUE)
        {
            continue;
        }
        NX_Solt destSlot;
        err = NX_ProcessCopySolt(NX_ThreadGetProcess((NX_Thread *)channel->serverThread), NX_ProcessCurrent(), soltBuf[i], &destSlot);
        if (err != NX_EOK) {
            soltBuf[i] = NX_SOLT_INVALID_VALUE;
        } else {
            soltBuf[i] = destSlot;
        }
    }

    NX_CopyToUser((char *)msg + capSlotsOffset, (char *)soltBuf, sizeof(NX_Solt) * slotCount);

    return soltBuf;
}

NX_PRIVATE void IpcCleanCap(NX_IpcChannel * channel, NX_Solt *solts, NX_Size count)
{
    for (int i = 0; i < count; i++)
    {
        if (solts[i] != NX_SOLT_INVALID_VALUE)
        {
            NX_ProcessUninstallSolt(NX_ThreadGetProcess((NX_Thread *)channel->serverThread), solts[i]);
        }
    }
    NX_MemFree(solts);
}

NX_PRIVATE NX_Error SysIpcCall(NX_Solt channelSolt, NX_IpcMsg * msg, NX_Size * returnValue)
{
    NX_Thread * server, * client;
    NX_Thread *cur = NX_CurrentThread;
    NX_IpcChannel * channel;
    NX_Process * process;
    NX_ExposedObject * exobj;
    NX_Size slotCount = 0;
    NX_Solt *soltBuf;

    if (channelSolt == NX_SOLT_INVALID_VALUE || !msg || !returnValue)
    {
        return NX_EINVAL;
    }

    process = NX_ProcessCurrent();
    if ((exobj = NX_ProcessGetSolt(process, channelSolt)) == NX_NULL)
    {
        return NX_ENOSRCH;
    }

    if (exobj->type != NX_EXOBJ_IPC_CHANNEL)
    {
        return NX_ENORES;
    }

    channel = (NX_IpcChannel *)exobj->object;
    NX_ASSERT(channel);

    /* copy client solt to msg */
    NX_CopyToUser((char *)&msg->clientSlot, (char *)&channel->clientSolt, sizeof(NX_Solt));

    soltBuf = IpcSendCap(channel, msg, &slotCount);

    channel->returnValue = 0; /* clear ret val before call */
    channel->state = NX_IPC_CHANNEL_ACTIVE;
    server = channel->serverThread;
    NX_ASSERT(server);
    client = channel->clientThread;
    NX_ASSERT(client);
    
    /* reset server state */
    server->stack = server->stackBase + server->stackSize - sizeof(NX_UArch);
    server->stack = NX_ContextInit(NX_UserThreadEntry, (void *)NX_ThreadExit,
        (void *)channel->buf.serverBufAddr, server->stack);

    /* sched to server */
    NX_UArch level = NX_IRQ_SaveLevel();
    NX_ThreadSetState(cur, NX_THREAD_BLOCKED);

    NX_SchedLockedIRQ(level, NX_NULL, server);

    if (returnValue)
    {
        NX_CopyToUser((char *)returnValue, (char *)&channel->returnValue, sizeof(NX_Size));
    }

    if (soltBuf != NX_NULL)
    {
        IpcCleanCap(channel, soltBuf, slotCount);
    }

    return NX_EOK;
}

NX_PRIVATE NX_Error SysIpcReturn(NX_Size returnValue)
{
    NX_Thread * self = NX_ThreadSelf();
    NX_IpcChannel * channel;
    
    channel = self->resource.ipcChannel;

    if (self->resource.ipcServer != NX_NULL || channel == NX_NULL)
    {
        return NX_EPERM;
    }

    channel->returnValue = returnValue;
    channel->state = NX_IPC_CHANNEL_DEACTIVE;
    
    /* sched to client */
    NX_UArch level = NX_IRQ_SaveLevel();
    NX_ThreadSetState(self, NX_THREAD_BLOCKED);
    NX_SchedLockedIRQ(level, NX_NULL, channel->clientThread);

    /**
     * server thread should never arrive here, but if channel closed,
     * will send a terminate signal to server thread, just return INTR.
     */
    return NX_EINTR;
}

NX_PRIVATE NX_Error FifoCloseSolt(void * object, NX_ExposedObjectType type)
{
    NX_Fifo * fifo = NX_NULL;

    if (type != NX_EXOBJ_FIFO)
    {
        return NX_ENORES;
    }

    fifo = (NX_Fifo *) object;
    NX_ASSERT(fifo);
    NX_FifoDestroy(fifo);
    return NX_EOK;
}

NX_PRIVATE NX_Error SysFifoCreate(NX_Size size, NX_Solt * outSolt)
{
    NX_Fifo * fifo;
    NX_Process * process;
    NX_Error err;
    NX_Solt solt = NX_SOLT_INVALID_VALUE;

    if (!size || !outSolt)
    {
        return NX_EINVAL;
    }

    fifo = NX_FifoCreate(size);
    if (fifo == NX_NULL)
    {
        return NX_ENOMEM;
    }

    process = NX_ThreadGetProcess(NX_ThreadSelf());
    if ((err = NX_ProcessInstallSolt(process, fifo, NX_EXOBJ_FIFO, FifoCloseSolt, &solt)) != NX_EOK)
    {
        NX_FifoDestroy(fifo);
        return err;
    }

    NX_CopyToUser((char *)outSolt, (char *)&solt, sizeof(solt));
    return NX_EOK;
}

NX_PRIVATE NX_Error SysFifoRead(NX_Solt solt, void *buf, NX_Size len, NX_Size *outLen)
{
    NX_Fifo * fifo = NX_NULL;
    NX_Process * process;
    NX_ExposedObject * exobj;
    NX_Size ret;
    
    if (solt == NX_SOLT_INVALID_VALUE)
    {
        return NX_EINVAL;
    }

    process = NX_ProcessCurrent();
    if ((exobj = NX_ProcessGetSolt(process, solt)) == NX_NULL)
    {
        return NX_ENOSRCH;
    }

    if (exobj->type != NX_EXOBJ_FIFO)
    {
        return NX_ENORES;
    }

    fifo = (NX_Fifo *)exobj->object;
    NX_ASSERT(fifo);

    if ((ret = NX_FifoRead(fifo, buf, len)) == 0)
    {
        return NX_ENORES;
    }

    NX_CopyToUser((char *)outLen, (char *)&ret, sizeof(ret));

    return NX_EOK;
}

NX_PRIVATE NX_Error SysFifoWrite(NX_Solt solt, void *buf, NX_Size len, NX_Size *outLen)
{
    NX_Fifo * fifo = NX_NULL;
    NX_Process * process;
    NX_ExposedObject * exobj;
    NX_Size ret;
    
    if (solt == NX_SOLT_INVALID_VALUE)
    {
        return NX_EINVAL;
    }

    process = NX_ProcessCurrent();
    if ((exobj = NX_ProcessGetSolt(process, solt)) == NX_NULL)
    {
        return NX_ENOSRCH;
    }

    if (exobj->type != NX_EXOBJ_FIFO)
    {
        return NX_ENORES;
    }

    fifo = (NX_Fifo *)exobj->object;
    NX_ASSERT(fifo);

    if ((ret = NX_FifoWrite(fifo, buf, len)) == 0)
    {
        return NX_EPERM;
    }

    NX_CopyToUser((char *)outLen, (char *)&ret, sizeof(ret));

    return NX_EOK;
}

NX_PRIVATE NX_Error SysFifoLength(NX_Solt solt, NX_Size *outLen)
{
    NX_Fifo * fifo = NX_NULL;
    NX_Process * process;
    NX_ExposedObject * exobj;
    NX_Size len;
    
    if (solt == NX_SOLT_INVALID_VALUE)
    {
        return NX_EINVAL;
    }

    process = NX_ProcessCurrent();
    if ((exobj = NX_ProcessGetSolt(process, solt)) == NX_NULL)
    {
        return NX_ENOSRCH;
    }

    if (exobj->type != NX_EXOBJ_FIFO)
    {
        return NX_ENORES;
    }

    fifo = (NX_Fifo *)exobj->object;
    NX_ASSERT(fifo);

    len = NX_FifoLen(fifo);

    NX_CopyToUser((char *)outLen, (char *)&len, sizeof(len));

    return NX_EOK;
}

NX_Error SysSoltCopy(NX_Solt dest, NX_Solt solt, NX_Solt * outSolt)
{
    NX_Process * srcProcess, * destProcess;
    NX_ExposedObject * srcExobj, * destExobj;
    NX_Solt newSolt = NX_SOLT_INVALID_VALUE;
    NX_Error err;
    
    if (solt < 0 || dest < 0 || !outSolt)
    {
        return NX_EINVAL;
    }

    /* get src exobj */
    srcProcess = NX_ProcessCurrent();
    if ((srcExobj = NX_ProcessGetSolt(srcProcess, solt)) == NX_NULL)
    {
        return NX_ENOSRCH;
    }

    /* get dst exobj from src process */
    if ((destExobj = NX_ProcessGetSolt(srcProcess, dest)) == NX_NULL)
    {
        return NX_ENOSRCH;
    }

    if (destExobj->type != NX_EXOBJ_THREAD)
    {
        return NX_EFAULT;
    }

    /* get dest process from dst object */
    destProcess = NX_ThreadGetProcess((NX_Thread *)destExobj->object);
    if (destProcess == NX_NULL)
    {
        return NX_EFAULT;
    }

    /* install object to dst process */
    if ((err = NX_ProcessCopySolt(destProcess, srcProcess, solt, &newSolt)) != NX_EOK)
    {
        return err;
    }

    NX_CopyToUser((char *)outSolt, (char *)&newSolt, sizeof(NX_Solt));

    return err;
}

NX_PRIVATE NX_Error ConditionCloseSolt(void * object, NX_ExposedObjectType type)
{
    NX_Condition * condition;

    if (type != NX_EXOBJ_CONDITION)
    {
        return NX_ENORES;
    }

    condition = (NX_Condition *) object;
    NX_ASSERT(condition);

    NX_ConditionBroadcast(condition);

    return NX_ConditionDestroy(condition);
}

NX_PRIVATE NX_Error SysConditionCreate(NX_Solt * outSolt, NX_U32 attr)
{
    NX_Condition * condition;
    NX_Error err;
    NX_Solt solt = NX_SOLT_INVALID_VALUE;
    NX_Process * process;
    
    if (!outSolt)
    {
        return NX_EINVAL;
    }

    condition = NX_ConditionCreate(attr);
    if (condition == NX_NULL)
    {
        return NX_ENOMEM;
    }

    process = NX_ProcessCurrent();
    if ((err = NX_ProcessInstallSolt(process, condition, NX_EXOBJ_CONDITION, ConditionCloseSolt, &solt)) != NX_EOK)
    {
        NX_MemFree(condition);
        return err;
    }

    NX_CopyToUser((char *)outSolt, (char *)&solt, sizeof(solt));

    return err;
}

NX_PRIVATE NX_Error SysConditionWait(NX_Solt solt, NX_Solt mutexSolt)
{
    NX_Condition * condition;
    NX_Mutex * mutex;
    NX_Process * process;
    NX_ExposedObject * exobj;
    NX_ExposedObject * exobjMutex;
    
    if (solt == NX_SOLT_INVALID_VALUE || mutexSolt == NX_SOLT_INVALID_VALUE)
    {
        return NX_EINVAL;
    }

    process = NX_ProcessCurrent();
    if ((exobj = NX_ProcessGetSolt(process, solt)) == NX_NULL)
    {
        return NX_ENOSRCH;
    }

    if ((exobjMutex = NX_ProcessGetSolt(process, mutexSolt)) == NX_NULL)
    {
        return NX_ENOSRCH;
    }

    if (exobj->type != NX_EXOBJ_CONDITION || exobjMutex->type != NX_EXOBJ_MUTEX)
    {
        return NX_ENORES;
    }

    condition = (NX_Condition *)exobj->object;
    NX_ASSERT(condition != NX_NULL);

    mutex = (NX_Mutex *)exobjMutex->object;
    NX_ASSERT(mutex != NX_NULL);

    return NX_ConditionWait(condition, mutex);
}

NX_PRIVATE NX_Error SysConditionWaitTimeout(NX_Solt solt, NX_Solt mutexSolt, NX_TimeVal tv)
{
    NX_Condition * condition;
    NX_Mutex * mutex;
    NX_Process * process;
    NX_ExposedObject * exobj;
    NX_ExposedObject * exobjMutex;
    
    if (solt == NX_SOLT_INVALID_VALUE || mutexSolt == NX_SOLT_INVALID_VALUE)
    {
        return NX_EINVAL;
    }

    process = NX_ProcessCurrent();
    if ((exobj = NX_ProcessGetSolt(process, solt)) == NX_NULL)
    {
        return NX_ENOSRCH;
    }

    if ((exobjMutex = NX_ProcessGetSolt(process, mutexSolt)) == NX_NULL)
    {
        return NX_ENOSRCH;
    }

    if (exobj->type != NX_EXOBJ_CONDITION || exobjMutex->type != NX_EXOBJ_MUTEX)
    {
        return NX_ENORES;
    }

    condition = (NX_Condition *)exobj->object;
    NX_ASSERT(condition != NX_NULL);

    mutex = (NX_Mutex *)exobjMutex->object;
    NX_ASSERT(mutex != NX_NULL);

    return NX_ConditionWaitTimeout(condition, mutex, tv);
}

NX_PRIVATE NX_Error SysConditionSignal(NX_Solt solt)
{
    NX_Condition * condition;
    NX_Process * process;
    NX_ExposedObject * exobj;
    
    if (solt == NX_SOLT_INVALID_VALUE)
    {
        return NX_EINVAL;
    }

    process = NX_ProcessCurrent();
    if ((exobj = NX_ProcessGetSolt(process, solt)) == NX_NULL)
    {
        return NX_ENOSRCH;
    }

    if (exobj->type != NX_EXOBJ_CONDITION)
    {
        return NX_ENORES;
    }

    condition = (NX_Condition *)exobj->object;
    NX_ASSERT(condition != NX_NULL);

    return NX_ConditionSignal(condition);
}

NX_PRIVATE NX_Error SysConditionBroadcast(NX_Solt solt)
{
    NX_Condition * condition;
    NX_Process * process;
    NX_ExposedObject * exobj;
    
    if (solt == NX_SOLT_INVALID_VALUE)
    {
        return NX_EINVAL;
    }

    process = NX_ProcessCurrent();
    if ((exobj = NX_ProcessGetSolt(process, solt)) == NX_NULL)
    {
        return NX_ENOSRCH;
    }

    if (exobj->type != NX_EXOBJ_CONDITION)
    {
        return NX_ENORES;
    }

    condition = (NX_Condition *)exobj->object;
    NX_ASSERT(condition != NX_NULL);

    return NX_ConditionBroadcast(condition);
}

NX_PRIVATE NX_Error SysProcessTrace(void)
{
    return NX_ENORES;
}

NX_PRIVATE NX_Error SysDeviceProbe(const char *name, NX_DeviceInfo *devinfo)
{
    NX_Error err;
    NX_DeviceInfo localDevinfo;
    
    err = NX_DeviceProbe(name, &localDevinfo);
    if (err == NX_EOK)
    {
        NX_CopyToUser((char *)devinfo, (char *)&localDevinfo, sizeof(NX_DeviceInfo));
    }
    return err;
}

NX_PRIVATE NX_Error SysDeviceEnum(NX_Offset offset, NX_DeviceInfo *devinfo)
{
    NX_Error err;
    NX_Device *dev;

    dev = NX_DeviceEnum(offset);

    if (dev != NX_NULL)
    {
        err = SysDeviceProbe(dev->name, devinfo);
    }
    else
    {
        err = NX_ENOSRCH;
    }
    return err;
}

NX_PRIVATE NX_Error MsgQueueCloseSolt(void * object, NX_ExposedObjectType type)
{
    NX_MsgQueue * msgQueue = NX_NULL;

    if (type != NX_EXOBJ_MSGQUEUE)
    {
        return NX_ENORES;
    }

    msgQueue = (NX_MsgQueue *) object;
    NX_ASSERT(msgQueue);
    NX_MegQueueDestroy(msgQueue);
    return NX_EOK;
}

NX_PRIVATE NX_Error SysMsgQueueCreate(NX_Size msgSize, NX_Size msgCount, NX_Solt * outSolt)
{
    NX_MsgQueue * msgQueue;
    NX_Process * process;
    NX_Error err;
    NX_Solt solt = NX_SOLT_INVALID_VALUE;

    msgQueue = NX_MsgQueueCreate(msgSize, msgCount);
    if (msgQueue == NX_NULL)
    {
        return NX_ENOMEM;
    }

    process = NX_ThreadGetProcess(NX_ThreadSelf());
    if ((err = NX_ProcessInstallSolt(process, msgQueue, NX_EXOBJ_MSGQUEUE, MsgQueueCloseSolt, &solt)) != NX_EOK)
    {
        NX_MegQueueDestroy(msgQueue);
        return err;
    }

    NX_CopyToUser((char *)outSolt, (char *)&solt, sizeof(solt));
    return NX_EOK;
}

NX_PRIVATE NX_Error SysMsgQueueSend(NX_Solt solt, NX_MsgBuf *buf)
{
    NX_MsgQueue * msgQueue;
    NX_Process * process;
    NX_ExposedObject * exobj;
    NX_MsgBuf kbuf;

    if (solt == NX_SOLT_INVALID_VALUE)
    {
        return NX_EINVAL;
    }

    process = NX_ProcessCurrent();
    if ((exobj = NX_ProcessGetSolt(process, solt)) == NX_NULL)
    {
        return NX_ENOSRCH;
    }

    if (exobj->type != NX_EXOBJ_MSGQUEUE)
    {
        return NX_ENORES;
    }

    msgQueue = (NX_MsgQueue *)exobj->object;
    NX_ASSERT(msgQueue);

    NX_CopyFromUser((char *)&kbuf, (char *)buf, sizeof(kbuf));

    return NX_MsgQueueSend(msgQueue, &kbuf);
}

NX_PRIVATE NX_Error SysMsgQueueTrySend(NX_Solt solt, NX_MsgBuf *buf)
{
    NX_MsgQueue * msgQueue;
    NX_Process * process;
    NX_ExposedObject * exobj;
    NX_MsgBuf kbuf;

    if (solt == NX_SOLT_INVALID_VALUE)
    {
        return NX_EINVAL;
    }

    process = NX_ProcessCurrent();
    if ((exobj = NX_ProcessGetSolt(process, solt)) == NX_NULL)
    {
        return NX_ENOSRCH;
    }

    if (exobj->type != NX_EXOBJ_MSGQUEUE)
    {
        return NX_ENORES;
    }

    msgQueue = (NX_MsgQueue *)exobj->object;
    NX_ASSERT(msgQueue);

    NX_CopyFromUser((char *)&kbuf, (char *)buf, sizeof(kbuf));

    return NX_MsgQueueTrySend(msgQueue, &kbuf);
}

NX_PRIVATE NX_Error SysMsgQueueRecv(NX_Solt solt, NX_MsgBuf *buf)
{
    NX_MsgQueue * msgQueue;
    NX_Process * process;
    NX_ExposedObject * exobj;
    NX_MsgBuf kbuf;

    if (solt == NX_SOLT_INVALID_VALUE)
    {
        return NX_EINVAL;
    }

    process = NX_ProcessCurrent();
    if ((exobj = NX_ProcessGetSolt(process, solt)) == NX_NULL)
    {
        return NX_ENOSRCH;
    }

    if (exobj->type != NX_EXOBJ_MSGQUEUE)
    {
        return NX_ENORES;
    }

    msgQueue = (NX_MsgQueue *)exobj->object;
    NX_ASSERT(msgQueue);

    NX_CopyFromUser((char *)&kbuf, (char *)buf, sizeof(kbuf));

    return NX_MsgQueueRecv(msgQueue, &kbuf);
}

NX_PRIVATE NX_Error SysMsgQueueTryRecv(NX_Solt solt, NX_MsgBuf *buf)
{
    NX_MsgQueue * msgQueue;
    NX_Process * process;
    NX_ExposedObject * exobj;
    NX_MsgBuf kbuf;

    if (solt == NX_SOLT_INVALID_VALUE)
    {
        return NX_EINVAL;
    }

    process = NX_ProcessCurrent();
    if ((exobj = NX_ProcessGetSolt(process, solt)) == NX_NULL)
    {
        return NX_ENOSRCH;
    }

    if (exobj->type != NX_EXOBJ_MSGQUEUE)
    {
        return NX_ENORES;
    }

    msgQueue = (NX_MsgQueue *)exobj->object;
    NX_ASSERT(msgQueue);

    NX_CopyFromUser((char *)&kbuf, (char *)buf, sizeof(kbuf));

    return NX_MsgQueueTryRecv(msgQueue, &kbuf);
}

NX_PRIVATE NX_Bool SysMsgQueueFull(NX_Solt solt)
{
    NX_MsgQueue * msgQueue;
    NX_Process * process;
    NX_ExposedObject * exobj;

    if (solt == NX_SOLT_INVALID_VALUE)
    {
        return NX_EINVAL;
    }

    process = NX_ProcessCurrent();
    if ((exobj = NX_ProcessGetSolt(process, solt)) == NX_NULL)
    {
        return NX_ENOSRCH;
    }

    if (exobj->type != NX_EXOBJ_MSGQUEUE)
    {
        return NX_ENORES;
    }

    msgQueue = (NX_MsgQueue *)exobj->object;
    NX_ASSERT(msgQueue);

    return NX_MsgQueueFull(msgQueue);
}

NX_PRIVATE NX_Bool SysMsgQueueEmpty(NX_Solt solt)
{
    NX_MsgQueue * msgQueue;
    NX_Process * process;
    NX_ExposedObject * exobj;

    if (solt == NX_SOLT_INVALID_VALUE)
    {
        return NX_EINVAL;
    }

    process = NX_ProcessCurrent();
    if ((exobj = NX_ProcessGetSolt(process, solt)) == NX_NULL)
    {
        return NX_ENOSRCH;
    }

    if (exobj->type != NX_EXOBJ_MSGQUEUE)
    {
        return NX_ENORES;
    }

    msgQueue = (NX_MsgQueue *)exobj->object;
    NX_ASSERT(msgQueue);

    return NX_MsgQueueEmpty(msgQueue);
}

NX_PRIVATE NX_Size SysMsgQueueLenght(NX_Solt solt)
{
    NX_MsgQueue * msgQueue;
    NX_Process * process;
    NX_ExposedObject * exobj;

    if (solt == NX_SOLT_INVALID_VALUE)
    {
        return NX_EINVAL;
    }

    process = NX_ProcessCurrent();
    if ((exobj = NX_ProcessGetSolt(process, solt)) == NX_NULL)
    {
        return NX_ENOSRCH;
    }

    if (exobj->type != NX_EXOBJ_MSGQUEUE)
    {
        return NX_ENORES;
    }

    msgQueue = (NX_MsgQueue *)exobj->object;
    NX_ASSERT(msgQueue);

    return NX_MsgQueueLenght(msgQueue);
}

NX_PRIVATE NX_Error SysSignalThread(NX_Solt threadSolt, NX_Solt msgSolt)
{
    NX_MsgQueue * msgQueue;
    NX_Thread * thread;
    NX_Process * process;
    NX_ExposedObject * exobj;

    if (threadSolt == NX_SOLT_INVALID_VALUE || msgSolt == NX_SOLT_INVALID_VALUE)
    {
        return NX_EINVAL;
    }

    process = NX_ProcessCurrent();

    if ((exobj = NX_ProcessGetSolt(process, threadSolt)) == NX_NULL)
    {
        return NX_ENOSRCH;
    }

    if (exobj->type != NX_EXOBJ_THREAD)
    {
        return NX_ENORES;
    }

    thread = (NX_Thread *)exobj->object;
    NX_ASSERT(thread);

    if ((exobj = NX_ProcessGetSolt(process, msgSolt)) == NX_NULL)
    {
        return NX_ENOSRCH;
    }

    if (exobj->type != NX_EXOBJ_MSGQUEUE)
    {
        return NX_ENORES;
    }

    msgQueue = (NX_MsgQueue *)exobj->object;
    NX_ASSERT(msgQueue);

    thread->resource.signals.signalMsgQueue = msgQueue;

    return NX_EOK;
}

NX_PRIVATE NX_Error PipeCloseSolt(void * object, NX_ExposedObjectType type)
{
    NX_Pipe * pipe = NX_NULL;

    if (type != NX_EXOBJ_PIPE)
    {
        return NX_ENORES;
    }

    pipe = (NX_Pipe *) object;
    NX_ASSERT(pipe);

    NX_PipeDisconnect(pipe);

    NX_PipeDecrease(pipe);

    return NX_EOK;
}

NX_PRIVATE NX_Error PipeCopySolt(void * object, NX_ExposedObjectType type)
{
    NX_Pipe * pipe = NX_NULL;

    if (type != NX_EXOBJ_PIPE)
    {
        return NX_ENORES;
    }

    pipe = (NX_Pipe *) object;
    NX_ASSERT(pipe);

    NX_PipeIncrease(pipe);
    NX_PipeConnectByPipe(pipe, NX_False);

    return NX_EOK;
}

NX_PRIVATE NX_Error SysPipeCreate(const char *name, NX_Size size, NX_U8 flags, NX_Solt * outSolt)
{
    NX_Pipe * pipe;
    NX_Process * process;
    NX_Error err;
    NX_Solt solt = NX_SOLT_INVALID_VALUE;

    pipe = NX_PipeCreate(name, size, flags);
    if (pipe == NX_NULL)
    {
        return NX_ENOMEM;
    }

    process = NX_ThreadGetProcess(NX_ThreadSelf());
    if ((err = NX_ProcessInstallSolt2(process, pipe, NX_EXOBJ_PIPE, PipeCloseSolt, PipeCopySolt, &solt)) != NX_EOK)
    {
        NX_PipeDestroy(pipe);
        return err;
    }

    NX_PipeIncrease(pipe);
    NX_PipeConnectByPipe(pipe, NX_PipeIsMaster(pipe));

    NX_CopyToUser((char *)outSolt, (char *)&solt, sizeof(solt));
    return NX_EOK;
}

NX_PRIVATE NX_Error SysPipeConnectByName(const char *name, NX_Solt * outSolt)
{
    NX_Pipe * pipe;
    NX_Process * process;
    NX_Error err;
    NX_Solt solt = NX_SOLT_INVALID_VALUE;

    pipe = NX_PipeConnectByName(name);
    if (pipe == NX_NULL)
    {
        return NX_ENOSRCH;
    }

    process = NX_ThreadGetProcess(NX_ThreadSelf());
    if ((err = NX_ProcessInstallSolt2(process, pipe, NX_EXOBJ_PIPE, PipeCloseSolt, PipeCopySolt, &solt)) != NX_EOK)
    {
        NX_PipeDisconnect(pipe);
        return err;
    }

    NX_PipeIncrease(pipe);

    NX_CopyToUser((char *)outSolt, (char *)&solt, sizeof(solt));
    return NX_EOK;
}

NX_PRIVATE NX_Error SysPipeSend(NX_Solt solt, void *buffer, NX_Size len, NX_Size *nsend)
{
    NX_Pipe * pipe;
    NX_Process * process;
    NX_ExposedObject * exobj;
    NX_Size ksend = 0;
    NX_Error err;

    if (solt == NX_SOLT_INVALID_VALUE)
    {
        return NX_EINVAL;
    }

    process = NX_ProcessCurrent();
    if ((exobj = NX_ProcessGetSolt(process, solt)) == NX_NULL)
    {
        return NX_ENOSRCH;
    }

    if (exobj->type != NX_EXOBJ_PIPE)
    {
        return NX_ENORES;
    }

    pipe = (NX_Pipe *)exobj->object;
    NX_ASSERT(pipe);

    err = NX_PipeSend(pipe, buffer, len, &ksend);
    if (err == NX_EOK)
    {
        NX_CopyToUser((char *)nsend, (char *)&ksend, sizeof(NX_Size));
    }
    return err;
}

NX_PRIVATE NX_Error SysPipeRecv(NX_Solt solt, void *buffer, NX_Size len, NX_Size *nrecv)
{
    NX_Pipe * pipe;
    NX_Process * process;
    NX_ExposedObject * exobj;
    NX_Size krecv = 0;
    NX_Error err;

    if (solt == NX_SOLT_INVALID_VALUE)
    {
        return NX_EINVAL;
    }

    process = NX_ProcessCurrent();
    if ((exobj = NX_ProcessGetSolt(process, solt)) == NX_NULL)
    {
        return NX_ENOSRCH;
    }

    if (exobj->type != NX_EXOBJ_PIPE)
    {
        return NX_ENORES;
    }

    pipe = (NX_Pipe *)exobj->object;
    NX_ASSERT(pipe);

    err = NX_PipeRecv(pipe, buffer, len, &krecv);
    if (err == NX_EOK)
    {
        NX_CopyToUser((char *)nrecv, (char *)&krecv, sizeof(NX_Size));
    }
    return err;
}

NX_PRIVATE NX_Error SysPipeDisconnect(NX_Solt solt)
{
    NX_Pipe * pipe;
    NX_Process * process;
    NX_ExposedObject * exobj;

    if (solt == NX_SOLT_INVALID_VALUE)
    {
        return NX_EINVAL;
    }

    process = NX_ProcessCurrent();
    if ((exobj = NX_ProcessGetSolt(process, solt)) == NX_NULL)
    {
        return NX_ENOSRCH;
    }

    if (exobj->type != NX_EXOBJ_PIPE)
    {
        return NX_ENORES;
    }

    pipe = (NX_Pipe *)exobj->object;
    NX_ASSERT(pipe);

    return NX_PipeDisconnect(pipe);
}

NX_Error SysSoltCopyTo(NX_Solt destThread, NX_Solt srcSolt, NX_Solt destSolt)
{
    NX_Process * srcProcess, * destProcess;
    NX_ExposedObject * srcExobj, * destExobj;
    NX_Error err;
    
    if (destThread < 0 || srcSolt < 0 || destSolt < 0)
    {
        return NX_EINVAL;
    }

    /* get src exobj */
    srcProcess = NX_ProcessCurrent();
    if ((srcExobj = NX_ProcessGetSolt(srcProcess, srcSolt)) == NX_NULL)
    {
        return NX_ENOSRCH;
    }

    /* get dst exobj from src process */
    if ((destExobj = NX_ProcessGetSolt(srcProcess, destThread)) == NX_NULL)
    {
        return NX_ENOSRCH;
    }

    if (destExobj->type != NX_EXOBJ_THREAD)
    {
        return NX_EFAULT;
    }

    /* get dest process from dst object */
    destProcess = NX_ThreadGetProcess((NX_Thread *)destExobj->object);
    if (destProcess == NX_NULL)
    {
        return NX_EFAULT;
    }

    /* install object to dst process */
    if ((err = NX_ProcessCopySoltTo(destProcess, srcProcess, destSolt, srcSolt)) != NX_EOK)
    {
        return err;
    }

    return err;
}

NX_PRIVATE NX_Error CloseUserObjectHandler(void * object, NX_ExposedObjectType type)
{
    NX_UserObject * uobj;

    if (type != NX_EXOBJ_UOBJECT)
    {
        return NX_ENORES;
    }

    uobj = (NX_UserObject *) object;
    NX_ASSERT(uobj);
    
    NX_SignalSend(uobj->pid, NX_SIGNAL_UOBJECT, uobj->object, NX_NULL);

    NX_UserObjectDestroy(uobj);

    return NX_EOK;
}

NX_Error SysSoltInstall(NX_Solt solt, void *object, NX_Solt * outSolt)
{
    NX_Process * process;
    NX_Thread *thread;
    NX_ExposedObject * exobj;
    NX_Solt newSolt = NX_SOLT_INVALID_VALUE;
    NX_Error err;
    NX_UserObject *uobj;
    
    if (solt < 0 || !outSolt)
    {
        return NX_EINVAL;
    }

    process = NX_ProcessCurrent();
    /* get exobj from process */
    if ((exobj = NX_ProcessGetSolt(process, solt)) == NX_NULL)
    {
        return NX_ENOSRCH;
    }

    if (exobj->type != NX_EXOBJ_THREAD)
    {
        return NX_EFAULT;
    }

    thread = (NX_Thread *)exobj->object;
    NX_ASSERT(thread);

    /* create on current process */
    uobj = NX_UserObjectCreate(object, NX_ThreadGetProcess(NX_ThreadSelf())->pid);
    if (!uobj)
    {
        return NX_ENOMEM;
    }

    /* install user object to dest */
    if ((err = NX_ProcessInstallSolt(NX_ThreadGetProcess(thread), uobj,
        NX_EXOBJ_UOBJECT, CloseUserObjectHandler, &newSolt)) != NX_EOK)
    {
        NX_UserObjectDestroy(uobj);
        return err;
    }
    
    NX_CopyToUser((char *)outSolt, (char *)&newSolt, sizeof(NX_Solt));

    return err;
}

NX_Error SysSoltUninstall(NX_Solt dest, NX_Solt solt)
{
    NX_Process * process;
    NX_Thread *thread;
    NX_ExposedObject * exobj;
    
    if (dest < 0 || solt < 0)
    {
        return NX_EINVAL;
    }

    process = NX_ProcessCurrent();
    /* get exobj from process */
    if ((exobj = NX_ProcessGetSolt(process, dest)) == NX_NULL)
    {
        return NX_ENOSRCH;
    }

    if (exobj->type != NX_EXOBJ_THREAD)
    {
        return NX_EFAULT;
    }

    thread = (NX_Thread *)exobj->object;
    NX_ASSERT(thread);

    return NX_ProcessUninstallSolt(NX_ThreadGetProcess(thread), solt);
}

NX_Error SysSoltObject(NX_Solt dest, NX_Solt solt, NX_UArch *outObject)
{
    NX_Process * process;
    NX_ExposedObject * exobj;
    NX_UserObject *uobj;
    NX_Thread *thread;
    NX_UArch value = -1; /* default invalid object value */
    NX_Error err = NX_EOK;
    
    if (dest < 0 || solt < 0 || !outObject)
    {
        err = NX_EINVAL;
        goto out;
    }

    process = NX_ProcessCurrent();
    /* get exobj from process */
    if ((exobj = NX_ProcessGetSolt(process, dest)) == NX_NULL)
    {
        err = NX_ENOSRCH;
        goto out;
    }

    if (exobj->type != NX_EXOBJ_THREAD)
    {
        err = NX_EFAULT;
        goto out;
    }

    thread = (NX_Thread *)exobj->object;
    NX_ASSERT(thread);

    /* get thread object */
    if ((exobj = NX_ProcessGetSolt(NX_ThreadGetProcess(thread), solt)) == NX_NULL)
    {
        err = NX_ENOSRCH;
        goto out;
    }

    if (exobj->type != NX_EXOBJ_UOBJECT)
    {
        err = NX_EFAULT;
        goto out;
    }

    uobj = (NX_UserObject *)exobj->object;
    NX_ASSERT(uobj);

    value = (NX_UArch)uobj->object;

out:
    NX_CopyToUser((char *)outObject, (char *)&value, sizeof(NX_UArch));

    return err;
}

/* xbook env syscall table  */
NX_PRIVATE const NX_SyscallHandler NX_SyscallTable[] = 
{
    SysInvalidCall,         /* 0 */
    SysDebugLog,            /* 1 */
    SysProcessExit,
    SysProcessLaunch,
    SysFileSystemMount,
    SysFileSystemUnmount,   /* 5 */
    SysFileSystemSync,
    SysFileOpen,
    SysInvalidCall,         /* !removed */
    SysFileRead,
    SysFileWrite,           /* 10 */
    SysFileSetPointer,
    SysFileSync,
    SysFileSetMode,
    SysFileGetStat,
    SysDirOpen,             /* 15 */
    SysInvalidCall,         /* !removed */
    SysDirRead,
    SysDirResetPointer,
    SysDirCreate,
    SysDirDelete,           /* 20 */
    SysFileRename,
    SysFileDelete,
    SysFileAccess,
    SysFileSetModeToPath,
    SysFileGetStatFromPath, /* 25 */
    SysHubRegister,
    SysHubUnregister,
    SysInvalidCall,         /* !removed */
    SysHubCallParamName,
    SysHubReturn,           /* 30 */
    SysHubWait,
    SysHubTranslate,
    SysFileIoctl,
    SysMemMap,
    SysMemUnmap,            /* 35 */
    SysMemHeap,
    SysProcessGetCwd,
    SysProcessSetCwd,
    SysSoltClose,
    SysSnapshotCreate,      /* 40 */
    SysSnapshotFirst,
    SysSnapshotNext,
    SysThreadSleep,
    SysClockGetMillisecond,
    SysTimeSet,             /* 45 */
    SysTimeGet,
    SysThreadCreate,
    SysThreadExit,
    SysThreadSuspend,
    SysThreadResume,        /* 50 */
    SysThreadWait,
    SysThreadTerminate,
    SysThreadGetId,
    SysThreadGetCurrentId,
    SysThreadGetCurrent,    /* 55 */
    SysThreadGetProcessId,
    SysSemaphoreCreate,
    SysSemaphoreDestroy,
    SysSemaphoreWait,
    SysSemaphoreTryWait,    /* 60 */
    SysSemaphoreSignal,
    SysSemaphoreSignalAll,
    SysSemaphoreGetValue,
    SysMutexCreate,
    SysMutexDestroy,        /* 65 */
    SysMutexAcquire,
    SysMutexTryAcquire,
    SysMutexRelease,
    SysMutexAcquirable,
    SysDeviceOpen,          /* 70 */
    SysDeviceRead,
    SysDeviceWrite,
    SysDeviceControl,
    SysSignalSend,
    SysSignalGetAttr,       /* 75 */
    SysSignalSetAttr,
    SysSignalContorl,
    SysTimerCreate,
    SysTimerStart,
    SysTimerStop,           /* 80 */
    SysDeviceMap,
    SysDriverRegister,
    SysThreadYield,
    SysPollWait,
    SysShareMemOpen,        /* 85 */
    SysShareMemMap,
    SysIpcBind,
    SysIpcConnect,
    SysIpcCall,             
    SysIpcReturn,           /* 90 */
    SysFifoCreate,
    SysFifoRead,
    SysFifoWrite,
    SysFifoLength,          
    SysSoltCopy,            /* 95 */
    SysMemMap2,
    SysConditionCreate,
    SysConditionWait,
    SysConditionWaitTimeout,
    SysConditionSignal,     /* 100 */
    SysConditionBroadcast,
    SysProcessTrace,
    SysDeviceProbe,
    SysDeviceEnum,
    SysMsgQueueCreate,      /* 105 */
    SysMsgQueueSend,
    SysMsgQueueTrySend,
    SysMsgQueueRecv,
    SysMsgQueueTryRecv,
    SysMsgQueueFull,        /* 110 */
    SysMsgQueueEmpty,
    SysMsgQueueLenght,
    SysSignalThread,
    SysPipeCreate,
    SysPipeConnectByName,   /* 115 */
    SysPipeDisconnect,
    SysPipeSend,
    SysPipeRecv,
    SysSoltCopyTo,
    SysSoltInstall,         /* 120 */
    SysSoltUninstall,
    SysSoltObject,
};

/* posix env syscall table */
NX_PRIVATE const NX_SyscallHandler NX_SyscallTablePosix[] = 
{
    SysInvalidCall,    /* 0 */
};

NX_PRIVATE const NX_SyscallHandler NX_SyscallTableWin32[] = 
{
    SysInvalidCall,    /* 0 */
};

NX_SyscallHandler NX_SyscallGetHandler(NX_SyscallApi api)
{
    NX_SyscallHandler handler = SysInvalidCall;

    NX_U32 callNumber = NX_SYSCALL_NUMBER(api);
    NX_U32 callEnv = NX_SYSCALL_ENV(api);
    
    switch (callEnv)
    {
    case NX_SYSCALL_ENV_XBOOK:
        if (callNumber < NX_ARRAY_SIZE(NX_SyscallTable))
        {
            handler = NX_SyscallTable[callNumber];
        }
        break;
    
    case NX_SYSCALL_ENV_POSIX:
        if (callNumber < NX_ARRAY_SIZE(NX_SyscallTablePosix))
        {
            handler = NX_SyscallTablePosix[callNumber];
        }
        break;

    case NX_SYSCALL_ENV_WIN32:
        if (callNumber < NX_ARRAY_SIZE(NX_SyscallTableWin32))
        {
            handler = NX_SyscallTableWin32[callNumber];
        }
        break;

    default:
        break;
    }

    return handler;
}

void NX_SyscallEnter(void)
{
    NX_Thread *cur = NX_ThreadSelf();
    if (cur != NX_NULL)
    {
        NX_AtomicInc(&cur->resource.syscallDepth);
    }
}

void NX_SyscallExit(void)
{
    NX_Thread *cur = NX_ThreadSelf();
    if (cur != NX_NULL)
    {
        NX_AtomicDec(&cur->resource.syscallDepth);
    }
}

NX_IArch NX_SyscallGetDepth(void)
{
    NX_Thread *cur = NX_ThreadSelf();
    return cur != NX_NULL ? NX_AtomicGet(&cur->resource.syscallDepth) : 0;
}
